Skip to content

Commit 6e2f5ab

Browse files
Log a warning for missing dependency versions (#1321)
1 parent bd0b376 commit 6e2f5ab

File tree

6 files changed

+115
-3
lines changed

6 files changed

+115
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Other improvements:
4343
- When the `publish.location` field is missing, `spago publish` will attempt to
4444
figure out the location from Git remotes and write it back to `spago.yaml`.
4545
- Internally Spago uses stricter-typed file paths.
46+
- `spago install` warns the user when the installed versions of packages are outside
47+
their specified dependency ranges.
4648
- `spago publish` no longer tries to validate all workspace dependencies, but
4749
only the (transitive) dependencies of the project being published.
4850

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ You can ask Spago to come up with a good set of bounds for you by running:
289289
$ spago install --ensure-ranges
290290
```
291291

292+
You can specify your version ranges manually in the `spago.yaml` configuration file too:
293+
294+
```yaml
295+
package:
296+
dependencies:
297+
- lists: ">=7.0.0 <8.0.0"
298+
```
299+
292300
### Install a direct dependency
293301

294302
To add a dependency to your project you can run:

src/Spago/Command/Fetch.purs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import Spago.FS as FS
5353
import Spago.Git as Git
5454
import Spago.Lock (LockEntry(..))
5555
import Spago.Lock as Lock
56+
import Spago.Log as Log
5657
import Spago.Path as Path
5758
import Spago.Paths as Paths
5859
import Spago.Purs as Purs
@@ -83,6 +84,12 @@ type FetchOpts =
8384
, isRepl :: Boolean
8485
}
8586

87+
type VersionResolution =
88+
{ name :: PackageName
89+
, requested :: Range
90+
, resolved :: Version
91+
}
92+
8693
run :: forall a. FetchOpts -> Spago (FetchEnv a) PackageTransitiveDeps
8794
run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do
8895
logDebug $ "Requested to install these packages: " <> printJson (CJ.array PackageName.codec) packagesRequestedToInstall
@@ -620,9 +627,50 @@ getTransitiveDeps workspacePackage = do
620627
<$> forEnv "core" depsRanges.core
621628
<*> forEnv "test" depsRanges.test
622629

623-
PackageSetBuild _info set -> do
624-
depsRanges # onEachEnvM \depsRanges' ->
625-
getTransitiveDepsFromPackageSet set $ (Array.fromFoldable $ Map.keys depsRanges')
630+
PackageSetBuild _info set ->
631+
do
632+
packages <- depsRanges # onEachEnvM \depsRanges' ->
633+
getTransitiveDepsFromPackageSet set $ (Array.fromFoldable $ Map.keys depsRanges')
634+
635+
let
636+
mergeEnvs :: k v. Ord k => ByEnv (Map k v) -> Map k v
637+
mergeEnvs { core, test } = Map.union core test
638+
639+
resolvePackageVersionsToRanges :: Map PackageName Package -> Map PackageName Range -> Array VersionResolution
640+
resolvePackageVersionsToRanges registry =
641+
Array.fromFoldable
642+
<<< Map.values
643+
<<< Map.mapMaybeWithKey \name requested ->
644+
Map.lookup name registry >>= case _ of
645+
RegistryVersion resolved -> Just { name, requested, resolved }
646+
_ -> Nothing
647+
648+
itemisePackages :: String -> Array (Tuple PackageName String) -> Array Docc
649+
itemisePackages heading pairs =
650+
Array.cons (toDoc heading) $ pairs <#> \(Tuple name version) ->
651+
Log.indent <<< toDoc
652+
$ "- "
653+
<> PackageName.print name
654+
<> ": "
655+
<> version
656+
657+
missingVersions =
658+
Array.filter
659+
(\{ requested, resolved } -> not $ Range.includes requested resolved)
660+
$ resolvePackageVersionsToRanges (mergeEnvs packages) (mergeEnvs depsRanges)
661+
662+
when (Array.length missingVersions > 0) do
663+
logWarn
664+
[ itemisePackages "The following package versions do not exist in your package set:"
665+
$ (\{ name, requested } -> Tuple name (Range.print requested))
666+
<$> missingVersions
667+
, [ Log.break ]
668+
, itemisePackages "Proceeding with the latest available versions instead:"
669+
$ (\{ name, resolved } -> Tuple name (Version.print resolved))
670+
<$> missingVersions
671+
]
672+
673+
pure packages
626674

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

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

test-fixtures/missing-versions.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
‼ The following package versions do not exist in your package set:
2+
- lists: >=1000.0.0 <1000.0.1
3+
- maybe: >=1000.0.0 <1000.0.1
4+
5+
6+
Proceeding with the latest available versions instead:
7+
- lists: 7.0.0
8+
- maybe: 6.0.0

test/Prelude.purs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Node.Platform as Platform
1818
import Node.Process as Process
1919
import Record (merge)
2020
import Registry.PackageName as PackageName
21+
import Registry.Range as Range
2122
import Registry.Version as Version
2223
import Spago.Cmd (ExecResult, StdinConfig(..))
2324
import Spago.Cmd (ExecResult, StdinConfig(..)) as X
@@ -250,6 +251,9 @@ mkPackageName = unsafeFromRight <<< PackageName.parse
250251
mkVersion :: String -> Version
251252
mkVersion = unsafeFromRight <<< Version.parse
252253

254+
mkRange :: String -> Range
255+
mkRange = unsafeFromRight <<< Range.parse
256+
253257
writeMain :: Array String -> String
254258
writeMain rest = writePursFile { moduleName: "Main", rest }
255259

test/Spago/Install.purs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Data.Map as Map
77
import Effect.Now as Now
88
import Registry.Version as Version
99
import Spago.Command.Init as Init
10+
import Spago.Core.Config (Dependencies(..), Config)
1011
import Spago.Core.Config as Config
1112
import Spago.FS as FS
1213
import Spago.Log (LogVerbosity(..))
@@ -17,6 +18,7 @@ import Test.Spec (Spec)
1718
import Test.Spec as Spec
1819
import Test.Spec.Assertions as Assert
1920
import Test.Spec.Assertions as Assertions
21+
import Test.Spec.Assertions.String (shouldContain)
2022

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

66+
Spec.it "warns when specified dependency versions do not exist" \{ spago, fixture, testCwd } -> do
67+
spago [ "init", "--package-set", "29.3.0" ] >>= shouldBeSuccess
68+
69+
FS.writeYamlFile Config.configCodec (testCwd </> "spago.yaml")
70+
$ insertConfigDependencies
71+
( Init.defaultConfig
72+
{ name: mkPackageName "aaa"
73+
, withWorkspace: Just { setVersion: Just $ unsafeFromRight $ Version.parse "0.0.1" }
74+
, testModuleName: "Test.Main"
75+
}
76+
)
77+
( Dependencies $ Map.fromFoldable
78+
[ Tuple (mkPackageName "prelude") (Just $ mkRange ">=6.0.0 <7.0.0")
79+
, Tuple (mkPackageName "lists") (Just $ mkRange ">=1000.0.0 <1000.0.1")
80+
]
81+
)
82+
( Dependencies $ Map.fromFoldable
83+
[ Tuple (mkPackageName "spec") (Just $ mkRange ">=7.0.0 <8.0.0")
84+
, Tuple (mkPackageName "maybe") (Just $ mkRange ">=1000.0.0 <1000.0.1")
85+
]
86+
)
87+
88+
warning <- FS.readTextFileSync $ fixture "missing-versions.txt"
89+
outputs <- spago [ "install" ]
90+
either _.stderr _.stderr outputs `shouldContain` warning
91+
6492
Spec.it "does not allow circular dependencies" \{ spago, fixture, testCwd } -> do
6593
spago [ "init" ] >>= shouldBeSuccess
6694
let
@@ -234,6 +262,19 @@ spec = Spec.around withTempDir do
234262
-- Check that the lockfile is back to the original
235263
checkFixture (testCwd </> "spago.lock") (fixture "spago.lock")
236264

265+
266+
insertConfigDependencies :: Config -> Dependencies -> Dependencies -> Config
267+
insertConfigDependencies config core test =
268+
( config
269+
{ package = config.package # map
270+
( \package' -> package'
271+
{ dependencies = core
272+
, test = package'.test # map ((_ { dependencies = test }))
273+
}
274+
)
275+
}
276+
)
277+
237278
writeConfigWithEither :: RootPath -> Aff Unit
238279
writeConfigWithEither root = do
239280
-- The commit for `either` is for the `v6.1.0` release

0 commit comments

Comments
 (0)