Skip to content

Commit

Permalink
Log a warning for missing dependency versions (#1321)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelLefkowitz authored Jan 30, 2025
1 parent bd0b376 commit 6e2f5ab
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Other improvements:
- When the `publish.location` field is missing, `spago publish` will attempt to
figure out the location from Git remotes and write it back to `spago.yaml`.
- Internally Spago uses stricter-typed file paths.
- `spago install` warns the user when the installed versions of packages are outside
their specified dependency ranges.
- `spago publish` no longer tries to validate all workspace dependencies, but
only the (transitive) dependencies of the project being published.

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ You can ask Spago to come up with a good set of bounds for you by running:
$ spago install --ensure-ranges
```

You can specify your version ranges manually in the `spago.yaml` configuration file too:

```yaml
package:
dependencies:
- lists: ">=7.0.0 <8.0.0"
```

### Install a direct dependency

To add a dependency to your project you can run:
Expand Down
55 changes: 52 additions & 3 deletions src/Spago/Command/Fetch.purs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import Spago.FS as FS
import Spago.Git as Git
import Spago.Lock (LockEntry(..))
import Spago.Lock as Lock
import Spago.Log as Log
import Spago.Path as Path
import Spago.Paths as Paths
import Spago.Purs as Purs
Expand Down Expand Up @@ -83,6 +84,12 @@ type FetchOpts =
, isRepl :: Boolean
}

type VersionResolution =
{ name :: PackageName
, requested :: Range
, resolved :: Version
}

run :: forall a. FetchOpts -> Spago (FetchEnv a) PackageTransitiveDeps
run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do
logDebug $ "Requested to install these packages: " <> printJson (CJ.array PackageName.codec) packagesRequestedToInstall
Expand Down Expand Up @@ -620,9 +627,50 @@ getTransitiveDeps workspacePackage = do
<$> forEnv "core" depsRanges.core
<*> forEnv "test" depsRanges.test

PackageSetBuild _info set -> do
depsRanges # onEachEnvM \depsRanges' ->
getTransitiveDepsFromPackageSet set $ (Array.fromFoldable $ Map.keys depsRanges')
PackageSetBuild _info set ->
do
packages <- depsRanges # onEachEnvM \depsRanges' ->
getTransitiveDepsFromPackageSet set $ (Array.fromFoldable $ Map.keys depsRanges')

let
mergeEnvs :: k v. Ord k => ByEnv (Map k v) -> Map k v
mergeEnvs { core, test } = Map.union core test

resolvePackageVersionsToRanges :: Map PackageName Package -> Map PackageName Range -> Array VersionResolution
resolvePackageVersionsToRanges registry =
Array.fromFoldable
<<< Map.values
<<< Map.mapMaybeWithKey \name requested ->
Map.lookup name registry >>= case _ of
RegistryVersion resolved -> Just { name, requested, resolved }
_ -> Nothing

itemisePackages :: String -> Array (Tuple PackageName String) -> Array Docc
itemisePackages heading pairs =
Array.cons (toDoc heading) $ pairs <#> \(Tuple name version) ->
Log.indent <<< toDoc
$ "- "
<> PackageName.print name
<> ": "
<> version

missingVersions =
Array.filter
(\{ requested, resolved } -> not $ Range.includes requested resolved)
$ resolvePackageVersionsToRanges (mergeEnvs packages) (mergeEnvs depsRanges)

when (Array.length missingVersions > 0) do
logWarn
[ itemisePackages "The following package versions do not exist in your package set:"
$ (\{ name, requested } -> Tuple name (Range.print requested))
<$> missingVersions
, [ Log.break ]
, itemisePackages "Proceeding with the latest available versions instead:"
$ (\{ name, resolved } -> Tuple name (Version.print resolved))
<$> missingVersions
]

pure packages

where
-- Note: here we can safely discard the dependencies because we don't need to bother about building a build plan,
Expand Down Expand Up @@ -784,3 +832,4 @@ onEachEnv f e = e { core = f e.core, test = f e.test }

onEachEnvM :: m a b. Apply m => (a -> m b) -> ByEnv a -> m (ByEnv b)
onEachEnvM f e = e { core = _, test = _ } <$> f e.core <*> f e.test

8 changes: 8 additions & 0 deletions test-fixtures/missing-versions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
‼ The following package versions do not exist in your package set:
- lists: >=1000.0.0 <1000.0.1
- maybe: >=1000.0.0 <1000.0.1


Proceeding with the latest available versions instead:
- lists: 7.0.0
- maybe: 6.0.0
4 changes: 4 additions & 0 deletions test/Prelude.purs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Node.Platform as Platform
import Node.Process as Process
import Record (merge)
import Registry.PackageName as PackageName
import Registry.Range as Range
import Registry.Version as Version
import Spago.Cmd (ExecResult, StdinConfig(..))
import Spago.Cmd (ExecResult, StdinConfig(..)) as X
Expand Down Expand Up @@ -250,6 +251,9 @@ mkPackageName = unsafeFromRight <<< PackageName.parse
mkVersion :: String -> Version
mkVersion = unsafeFromRight <<< Version.parse

mkRange :: String -> Range
mkRange = unsafeFromRight <<< Range.parse

writeMain :: Array String -> String
writeMain rest = writePursFile { moduleName: "Main", rest }

Expand Down
41 changes: 41 additions & 0 deletions test/Spago/Install.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Data.Map as Map
import Effect.Now as Now
import Registry.Version as Version
import Spago.Command.Init as Init
import Spago.Core.Config (Dependencies(..), Config)
import Spago.Core.Config as Config
import Spago.FS as FS
import Spago.Log (LogVerbosity(..))
Expand All @@ -17,6 +18,7 @@ import Test.Spec (Spec)
import Test.Spec as Spec
import Test.Spec.Assertions as Assert
import Test.Spec.Assertions as Assertions
import Test.Spec.Assertions.String (shouldContain)

spec :: Spec Unit
spec = Spec.around withTempDir do
Expand Down Expand Up @@ -61,6 +63,32 @@ spec = Spec.around withTempDir do
spago [ "install", "foo-foo-foo", "bar-bar-bar", "effcet", "arrys" ] >>= shouldBeFailureErr (fixture "missing-dependencies.txt")
checkFixture (testCwd </> "spago.yaml") (fixture "spago-install-failure.yaml")

Spec.it "warns when specified dependency versions do not exist" \{ spago, fixture, testCwd } -> do
spago [ "init", "--package-set", "29.3.0" ] >>= shouldBeSuccess

FS.writeYamlFile Config.configCodec (testCwd </> "spago.yaml")
$ insertConfigDependencies
( Init.defaultConfig
{ name: mkPackageName "aaa"
, withWorkspace: Just { setVersion: Just $ unsafeFromRight $ Version.parse "0.0.1" }
, testModuleName: "Test.Main"
}
)
( Dependencies $ Map.fromFoldable
[ Tuple (mkPackageName "prelude") (Just $ mkRange ">=6.0.0 <7.0.0")
, Tuple (mkPackageName "lists") (Just $ mkRange ">=1000.0.0 <1000.0.1")
]
)
( Dependencies $ Map.fromFoldable
[ Tuple (mkPackageName "spec") (Just $ mkRange ">=7.0.0 <8.0.0")
, Tuple (mkPackageName "maybe") (Just $ mkRange ">=1000.0.0 <1000.0.1")
]
)

warning <- FS.readTextFileSync $ fixture "missing-versions.txt"
outputs <- spago [ "install" ]
either _.stderr _.stderr outputs `shouldContain` warning

Spec.it "does not allow circular dependencies" \{ spago, fixture, testCwd } -> do
spago [ "init" ] >>= shouldBeSuccess
let
Expand Down Expand Up @@ -234,6 +262,19 @@ spec = Spec.around withTempDir do
-- Check that the lockfile is back to the original
checkFixture (testCwd </> "spago.lock") (fixture "spago.lock")


insertConfigDependencies :: Config -> Dependencies -> Dependencies -> Config
insertConfigDependencies config core test =
( config
{ package = config.package # map
( \package' -> package'
{ dependencies = core
, test = package'.test # map ((_ { dependencies = test }))
}
)
}
)

writeConfigWithEither :: RootPath -> Aff Unit
writeConfigWithEither root = do
-- The commit for `either` is for the `v6.1.0` release
Expand Down

0 comments on commit 6e2f5ab

Please sign in to comment.