diff --git a/README.md b/README.md index 9ba2b2edd..e6de6b1ab 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ Where to go from here? There are a few places you should check out: - [Override a package in the package set with a remote one](#override-a-package-in-the-package-set-with-a-remote-one) - [Add a package to the package set](#add-a-package-to-the-package-set) - [Querying package sets](#querying-package-sets) + - [Querying package information](#querying-package-information) - [Upgrading packages and the package set](#upgrading-packages-and-the-package-set) - [Custom package sets](#custom-package-sets) - [Graph the project modules and dependencies](#graph-the-project-modules-and-dependencies) @@ -621,6 +622,57 @@ $ spago registry package-sets --latest +---------+------------+----------+ ``` +You can also list all the packages contained in a specific package set by providing the set version as an argument: + +```console +$ spago registry package-sets 0.0.1 ++------------------------------+---------+ +| PACKAGE | VERSION | ++------------------------------+---------+ +| ace | 9.0.0 | +| aff | 7.1.0 | +| aff-bus | 6.0.0 | +... +``` + +This is useful to check what version of a package is included in a specific set, or to explore the contents of an older set. The `--json` flag is available for machine-friendly output. + +### Querying package information + +To get detailed information about a package from the Registry, use `spago registry info`: + +```console +$ spago registry info aff +``` + +This will print the package metadata (location, published versions, etc.) as YAML, followed by a table showing which package sets contain each published version of the package: + +``` +location: + githubOwner: purescript-contrib + githubRepo: purescript-aff +published: + 7.1.0: + bytes: 44616 + hash: sha256-... + publishedTime: 2022-05-12T16:39:07.000Z + ref: v7.1.0 +... + +Package Sets containing each version: ++---------+-------------------------------+ +| VERSION | PACKAGE SETS | ++---------+-------------------------------+ +| 7.1.0 | 0.0.1, 0.0.2, 0.1.0, ... | ++---------+-------------------------------+ +``` + +Use the `--json` flag to get the output in JSON format, which includes both the metadata and the package sets information in a structured format: + +```console +$ spago registry info aff --json +``` + ### Upgrading packages and the package set If your project is using the Registry solver (i.e. no package set and only version bounds), then running `spago upgrade` diff --git a/bin/src/Flags.purs b/bin/src/Flags.purs index cae73a8c6..46d382780 100644 --- a/bin/src/Flags.purs +++ b/bin/src/Flags.purs @@ -294,6 +294,14 @@ maybeSetVersion = <> O.help "Optional package set version to be used instead of the latest one" ) +maybePackageSetVersion :: Parser (Maybe String) +maybePackageSetVersion = + OT.optional $ + O.strArgument + ( O.metavar "SET" + <> O.help "Package set version to query" + ) + maybePackageName :: Parser (Maybe String) maybePackageName = OT.optional $ diff --git a/bin/src/Main.purs b/bin/src/Main.purs index 3c4b1b8bb..562d69ede 100644 --- a/bin/src/Main.purs +++ b/bin/src/Main.purs @@ -457,6 +457,7 @@ registryPackageSetsArgsParser = Optparse.fromRecord { json: Flags.json , latest: Flags.latest + , set: Flags.maybePackageSetVersion } registryTransferArgsParser :: Parser RegistryTransferArgs diff --git a/src/Spago/Command/Registry.purs b/src/Spago/Command/Registry.purs index 1c5bb8b91..e9464deba 100644 --- a/src/Spago/Command/Registry.purs +++ b/src/Spago/Command/Registry.purs @@ -95,49 +95,118 @@ info { package, json } = do Left err -> do logDebug err die $ "Could not find package " <> PackageName.print packageName - Right meta -> do - -- We just print out the metadata file - output case json of - true -> OutputJson Metadata.codec meta - false -> OutputYaml Metadata.codec meta + Right meta@(Metadata.Metadata { published }) -> do + -- Get package sets for each published version + { db } <- ask + let versions = Array.fromFoldable $ Map.keys published + packageSetsByVersion <- for versions \version -> do + entries <- liftEffect $ Db.selectPackageSetEntriesByPackage db packageName version + let setVersions = map _.packageSetVersion entries + pure { version, packageSets: Array.sort setVersions } + + case json of + true -> do + -- For JSON, we include package sets in a combined output + let + versionSetsCodec = CJ.named "VersionPackageSets" $ CJ.Record.object + { version: Version.codec + , packageSets: CJ.array Version.codec + } + combinedCodec = CJ.named "PackageInfoWithSets" $ CJ.Record.object + { metadata: Metadata.codec + , packageSets: CJ.array versionSetsCodec + } + output $ OutputJson combinedCodec { metadata: meta, packageSets: packageSetsByVersion } + false -> do + -- For YAML/text output, print metadata then add package sets table + output $ OutputYaml Metadata.codec meta + -- Only show the package sets table if there are any + let nonEmptySets = Array.filter (\r -> not (Array.null r.packageSets)) packageSetsByVersion + when (not (Array.null nonEmptySets)) do + logInfo "" + logInfo "Package Sets containing each version:" + output $ OutputTable + { titles: [ "VERSION", "PACKAGE SETS" ] + , rows: nonEmptySets # map \{ version, packageSets: sets } -> + [ Version.print version + , String.joinWith ", " (map Version.print sets) + ] + } type RegistryPackageSetsArgs = { latest :: Boolean , json :: Boolean + , set :: Maybe String } packageSets :: ∀ r. RegistryPackageSetsArgs -> Spago (RegistryEnv r) Unit -packageSets { latest, json } = do - availableSets <- Registry.listPackageSets +packageSets { latest, json, set } = do + case set of + Just setVersionStr -> do + -- Query packages in a specific package set + setVersion <- case parseLenientVersion setVersionStr of + Left err -> die [ "Could not parse package set version. Error:", show err ] + Right v -> pure v - let - sets = case latest of - false -> availableSets - true -> - -- here we need to keep only the highest version of all the sets with the same compiler version - Array.fromFoldable - $ Map.values - $ - foldl - ( \acc newSet -> case Map.lookup newSet.compiler acc of - Nothing -> Map.insert newSet.compiler newSet acc - Just { version } -> case newSet.version > version of - true -> Map.insert newSet.compiler newSet acc - false -> acc - ) - Map.empty - availableSets - - output case json of - true -> OutputJson (CJ.array Db.packageSetCodec) sets - false -> OutputTable - { titles: [ "VERSION", "DATE", "COMPILER" ] - , rows: sets # map \{ version, date, compiler } -> - [ Version.print version - , DateTime.format Internal.Format.iso8601Date $ DateTime date bottom - , Version.print compiler - ] - } + { db } <- ask + entries <- liftEffect $ Db.selectPackageSetEntriesBySet db setVersion + + when (Array.null entries) do + die $ "No packages found in package set " <> Version.print setVersion + + -- Sort entries by package name for consistent output + let sortedEntries = Array.sortWith _.packageName entries + + output case json of + true -> + let + entryCodec = CJ.named "PackageSetEntryOutput" $ CJ.Record.object + { packageName: PackageName.codec + , packageVersion: Version.codec + } + toOutput e = { packageName: e.packageName, packageVersion: e.packageVersion } + in + OutputJson (CJ.array entryCodec) (map toOutput sortedEntries) + false -> OutputTable + { titles: [ "PACKAGE", "VERSION" ] + , rows: sortedEntries # map \{ packageName, packageVersion } -> + [ PackageName.print packageName + , Version.print packageVersion + ] + } + + Nothing -> do + -- Original behavior: list all package sets + availableSets <- Registry.listPackageSets + + let + sets = case latest of + false -> availableSets + true -> + -- here we need to keep only the highest version of all the sets with the same compiler version + Array.fromFoldable + $ Map.values + $ + foldl + ( \acc newSet -> case Map.lookup newSet.compiler acc of + Nothing -> Map.insert newSet.compiler newSet acc + Just { version } -> case newSet.version > version of + true -> Map.insert newSet.compiler newSet acc + false -> acc + ) + Map.empty + availableSets + + output case json of + true -> OutputJson (CJ.array Db.packageSetCodec) sets + false -> OutputTable + { titles: [ "VERSION", "DATE", "COMPILER" ] + , rows: sets # map \{ version, date, compiler } -> + [ Version.print version + , DateTime.format Internal.Format.iso8601Date $ DateTime date bottom + , Version.print compiler + ] + } type RegistryTransferArgs = { privateKeyPath :: RawFilePath } diff --git a/src/Spago/Db.purs b/src/Spago/Db.purs index 979ad6aaa..8c647c1cf 100644 --- a/src/Spago/Db.purs +++ b/src/Spago/Db.purs @@ -14,8 +14,11 @@ module Spago.Db , insertPackageSet , insertPackageSetEntry , packageSetCodec + , packageSetEntryCodec , selectLatestPackageSetByCompiler , selectPackageSets + , selectPackageSetEntriesBySet + , selectPackageSetEntriesByPackage , updateLastPull ) where @@ -70,10 +73,6 @@ selectLatestPackageSetByCompiler db compiler = do maybePackageSet <- Nullable.toMaybe <$> Uncurried.runEffectFn2 selectLatestPackageSetByCompilerImpl db (Version.print compiler) pure $ packageSetFromJs =<< maybePackageSet -{- - -We'll need these when implementing a command for "show me what's in this package set" - selectPackageSetEntriesBySet :: Db -> Version -> Effect (Array PackageSetEntry) selectPackageSetEntriesBySet db packageSetVersion = do packageSetEntries <- Uncurried.runEffectFn2 selectPackageSetEntriesBySetImpl db (Version.print packageSetVersion) @@ -83,7 +82,6 @@ selectPackageSetEntriesByPackage :: Db -> PackageName -> Version -> Effect (Arra selectPackageSetEntriesByPackage db packageName version = do packageSetEntries <- Uncurried.runEffectFn3 selectPackageSetEntriesByPackageImpl db (PackageName.print packageName) (Version.print version) pure $ Array.mapMaybe packageSetEntryFromJs packageSetEntries --} getLastPull :: Db -> String -> Effect (Maybe DateTime) getLastPull db key = do @@ -208,8 +206,6 @@ packageSetEntryToJs { packageSetVersion, packageName, packageVersion } = , packageVersion: Version.print packageVersion } -{- - packageSetEntryFromJs :: PackageSetEntryJs -> Maybe PackageSetEntry packageSetEntryFromJs p = hush do packageSetVersion <- Version.parse p.packageSetVersion @@ -217,8 +213,6 @@ packageSetEntryFromJs p = hush do packageVersion <- Version.parse p.packageVersion pure $ { packageSetVersion, packageName, packageVersion } --} - -------------------------------------------------------------------------------- -- Codecs @@ -229,6 +223,13 @@ packageSetCodec = CJ.named "PackageSet" $ CJ.Record.object , compiler: Version.codec } +packageSetEntryCodec :: CJ.Codec PackageSetEntry +packageSetEntryCodec = CJ.named "PackageSetEntry" $ CJ.Record.object + { packageSetVersion: Version.codec + , packageName: PackageName.codec + , packageVersion: Version.codec + } + -------------------------------------------------------------------------------- -- FFI diff --git a/test-fixtures/registry-package-set-0.0.1.txt b/test-fixtures/registry-package-set-0.0.1.txt new file mode 100644 index 000000000..8e3401720 --- /dev/null +++ b/test-fixtures/registry-package-set-0.0.1.txt @@ -0,0 +1,50 @@ ++------------------------------+---------+ +| PACKAGE | VERSION | ++------------------------------+---------+ +| ace | 9.0.0 | +| aff | 7.1.0 | +| aff-bus | 6.0.0 | +| aff-coroutines | 9.0.0 | +| aff-promise | 4.0.0 | +| aff-retry | 2.0.0 | +| affjax | 13.0.0 | +| affjax-node | 1.0.0 | +| affjax-web | 1.0.0 | +| ansi | 7.0.0 | +| argonaut | 9.0.0 | +| argonaut-codecs | 9.1.0 | +| argonaut-core | 7.0.0 | +| argonaut-generic | 8.0.0 | +| argonaut-traversals | 10.0.0 | +| argparse-basic | 2.0.0 | +| array-builder | 0.1.2 | +| arraybuffer | 13.0.0 | +| arraybuffer-builder | 3.0.1 | +| arraybuffer-types | 3.0.2 | +| arrays | 7.1.0 | +| arrays-zipper | 2.0.1 | +| ask | 1.0.0 | +| assert | 6.0.0 | +| avar | 5.0.0 | +| b64 | 0.0.8 | +| barbies | 1.0.1 | +| barlow-lens | 0.9.0 | +| bifunctors | 6.0.0 | +| bigints | 7.0.1 | +| bolson | 0.1.1 | +| bower-json | 3.0.0 | +| call-by-name | 4.0.1 | +| canvas | 6.0.0 | +| canvas-action | 9.0.0 | +| cartesian | 1.0.6 | +| catenable-lists | 7.0.0 | +| channel | 1.0.0 | +| checked-exceptions | 3.1.1 | +| classnames | 2.0.0 | +| codec | 5.0.0 | +| codec-argonaut | 9.0.0 | +| colors | 7.0.1 | +| concur-core | 0.5.0 | +| concur-react | 0.5.0 | +| concurrent-queues | 3.0.0 | +| console | 6.0.0 | diff --git a/test/Spago/Registry.purs b/test/Spago/Registry.purs index 1ade397f7..6bd9645b6 100644 --- a/test/Spago/Registry.purs +++ b/test/Spago/Registry.purs @@ -35,3 +35,28 @@ spec = Spec.around withTempDir do # String.joinWith "\n" } shouldBeSuccessOutput (fixture "registry-list-package-sets-latest.txt") $ bimap updateStdout updateStdout result + + Spec.it "query package sets and package info with set associations" \{ spago, fixture } -> do + -- Test: List packages in a specific package set (table output) + do + result <- spago [ "registry", "package-sets", "0.0.1" ] + let + updateStdout r = r + { stdout = r.stdout + # String.split (Pattern "\n") + # Array.take 50 + # String.joinWith "\n" + } + shouldBeSuccessOutput (fixture "registry-package-set-0.0.1.txt") $ bimap updateStdout updateStdout result + + -- Test: List packages in a specific package set (JSON output) + spago [ "registry", "package-sets", "0.0.1", "--json" ] >>= shouldBeSuccess + + -- Test: Error for non-existent package set version + spago [ "registry", "package-sets", "999.999.999" ] >>= shouldBeFailure + + -- Test: Info command shows package sets for versions (text output) + spago [ "registry", "info", "prelude" ] >>= shouldBeSuccess + + -- Test: Info command with JSON includes package sets + spago [ "registry", "info", "prelude", "--json" ] >>= shouldBeSuccess