Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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`
Expand Down
8 changes: 8 additions & 0 deletions bin/src/Flags.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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 $
Expand Down
1 change: 1 addition & 0 deletions bin/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ registryPackageSetsArgsParser =
Optparse.fromRecord
{ json: Flags.json
, latest: Flags.latest
, set: Flags.maybePackageSetVersion
}

registryTransferArgsParser :: Parser RegistryTransferArgs
Expand Down
139 changes: 104 additions & 35 deletions src/Spago/Command/Registry.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is reading directly from the database without a prior getRegistry or fetchRegistry — the Nothing branch calls Registry.listPackageSets so that's ok bc it hydrates the daatabase, but this branch doesn't. Do we need to make sure this branch ensures we're up-to-date in the db, or is that happening somewhere else?


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 }

Expand Down
19 changes: 10 additions & 9 deletions src/Spago/Db.purs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ module Spago.Db
, insertPackageSet
, insertPackageSetEntry
, packageSetCodec
, packageSetEntryCodec
, selectLatestPackageSetByCompiler
, selectPackageSets
, selectPackageSetEntriesBySet
, selectPackageSetEntriesByPackage
, updateLastPull
) where

Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -208,17 +206,13 @@ packageSetEntryToJs { packageSetVersion, packageName, packageVersion } =
, packageVersion: Version.print packageVersion
}

{-

packageSetEntryFromJs :: PackageSetEntryJs -> Maybe PackageSetEntry
packageSetEntryFromJs p = hush do
packageSetVersion <- Version.parse p.packageSetVersion
packageName <- PackageName.parse p.packageName
packageVersion <- Version.parse p.packageVersion
pure $ { packageSetVersion, packageName, packageVersion }

-}

--------------------------------------------------------------------------------
-- Codecs

Expand All @@ -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

Expand Down
50 changes: 50 additions & 0 deletions test-fixtures/registry-package-set-0.0.1.txt
Original file line number Diff line number Diff line change
@@ -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 |
25 changes: 25 additions & 0 deletions test/Spago/Registry.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading