From 16502a1b8ef01f51bf7164d6d1c767ae8ad1f792 Mon Sep 17 00:00:00 2001 From: Fyodor Soikin Date: Wed, 28 Aug 2024 11:41:51 -0400 Subject: [PATCH] Allow to publish packages with test dependencies not in the registry (#1270) --- CHANGELOG.md | 2 + spago.lock | 1785 +++++++++-------- src/Spago/Command/Build.purs | 13 +- src/Spago/Command/Fetch.purs | 428 ++-- src/Spago/Command/Publish.purs | 20 +- src/Spago/Config.purs | 9 +- src/Spago/Lock.purs | 25 +- .../local-package-set-lockfile/spago.lock.new | 21 +- .../local-package-set-lockfile/spago.lock.old | 21 +- .../package-b/spago.yaml | 2 - .../package-c/spago.yaml | 1 - .../package-c/test/Main.purs | 11 + test-fixtures/polyrepo.lock | 79 +- .../publish-extra-package-core-dependency.txt | 20 + .../publish/extra-package-core/spago.yaml | 30 + .../publish/extra-package-core/src/Main.purs | 10 + .../publish/extra-package-core/test/Main.purs | 8 + .../publish/extra-package-test/spago.yaml | 25 + .../publish/extra-package-test/src/Main.purs | 9 + .../publish/extra-package-test/test/Main.purs | 11 + test-fixtures/spago-with-maybe.lock | 35 +- test-fixtures/spago.lock | 21 +- test/Spago/Lock.purs | 71 +- test/Spago/Publish.purs | 14 + 24 files changed, 1522 insertions(+), 1149 deletions(-) create mode 100644 test-fixtures/monorepo/case3-shared-dependencies2/package-c/test/Main.purs create mode 100644 test-fixtures/publish-extra-package-core-dependency.txt create mode 100644 test-fixtures/publish/extra-package-core/spago.yaml create mode 100644 test-fixtures/publish/extra-package-core/src/Main.purs create mode 100644 test-fixtures/publish/extra-package-core/test/Main.purs create mode 100644 test-fixtures/publish/extra-package-test/spago.yaml create mode 100644 test-fixtures/publish/extra-package-test/src/Main.purs create mode 100644 test-fixtures/publish/extra-package-test/test/Main.purs diff --git a/CHANGELOG.md b/CHANGELOG.md index fc55b3730..d194a6ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ Other improvements: overwrite the file if the marker isn't present, assuming that the file was manually created or edited, not generated by Spago itself. - migrated tests to the `spec-node` runner. +- `spago publish` now allows to publish a package with some test (but only + test!) dependencies not present in the registry. ## [0.21.0] - 2023-05-04 diff --git a/spago.lock b/spago.lock index ea0b151e9..1d0f00734 100644 --- a/spago.lock +++ b/spago.lock @@ -2,840 +2,977 @@ workspace: packages: docs-search-client-halogen: path: docs-search/client-halogen - dependencies: - - aff - - aff-promise - - arrays - - codec-json - - control - - css - - docs-search-common - - effect - - either - - foldable-traversable - - halogen - - halogen-css - - halogen-subscriptions - - js-uri - - json - - language-purescript - - lists - - markdown-it - - markdown-it-halogen - - maybe - - newtype - - ordered-collections - - prelude - - profunctor-lenses - - search-trie - - strings - - tuples - - web-dom - - web-events - - web-html - - web-storage - - web-uievents - test_dependencies: [] - build_plan: - - aff - - aff-promise - - argonaut-core - - arrays - - avar - - bifunctors - - catenable-lists - - codec - - codec-json - - colors - - console - - const - - contravariant - - control - - css - - datetime - - distributive - - docs-search-common - - dom-indexed - - effect - - either - - enums - - exceptions - - exists - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - fork - - formatters - - free - - freeap - - functions - - functors - - gen - - halogen - - halogen-css - - halogen-subscriptions - - halogen-vdom - - html-parser-halogen - - identity - - integers - - invariant - - js-date - - js-promise - - js-uri - - json - - json-codecs - - language-purescript - - lazy - - lcg - - lists - - markdown-it - - markdown-it-halogen - - maybe - - media-types - - newtype - - nonempty - - now - - nullable - - numbers - - options - - ordered-collections - - orders - - parallel - - parsing - - partial - - prelude - - profunctor - - profunctor-lenses - - quickcheck - - quickcheck-laws - - random - - record - - refs - - safe-coerce - - search-trie - - st - - string-parsers - - strings - - tailrec - - these - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - unsafe-reference - - variant - - versions - - web-clipboard - - web-dom - - web-events - - web-file - - web-html - - web-pointerevents - - web-storage - - web-touchevents - - web-uievents + core: + dependencies: + - aff + - aff-promise + - arrays + - codec-json + - control + - css + - docs-search-common + - effect + - either + - foldable-traversable + - halogen + - halogen-css + - halogen-subscriptions + - js-uri + - json + - language-purescript + - lists + - markdown-it + - markdown-it-halogen + - maybe + - newtype + - ordered-collections + - prelude + - profunctor-lenses + - search-trie + - strings + - tuples + - web-dom + - web-events + - web-html + - web-storage + - web-uievents + build_plan: + - aff + - aff-promise + - argonaut-core + - arrays + - avar + - bifunctors + - catenable-lists + - codec + - codec-json + - colors + - console + - const + - contravariant + - control + - css + - datetime + - distributive + - docs-search-common + - dom-indexed + - effect + - either + - enums + - exceptions + - exists + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - fork + - formatters + - free + - freeap + - functions + - functors + - gen + - halogen + - halogen-css + - halogen-subscriptions + - halogen-vdom + - html-parser-halogen + - identity + - integers + - invariant + - js-date + - js-promise + - js-uri + - json + - json-codecs + - language-purescript + - lazy + - lcg + - lists + - markdown-it + - markdown-it-halogen + - maybe + - media-types + - newtype + - nonempty + - now + - nullable + - numbers + - options + - ordered-collections + - orders + - parallel + - parsing + - partial + - prelude + - profunctor + - profunctor-lenses + - quickcheck + - quickcheck-laws + - random + - record + - refs + - safe-coerce + - search-trie + - st + - string-parsers + - strings + - tailrec + - these + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - unsafe-reference + - variant + - versions + - web-clipboard + - web-dom + - web-events + - web-file + - web-html + - web-pointerevents + - web-storage + - web-touchevents + - web-uievents + test: + dependencies: [] + build_plan: [] docs-search-common: path: docs-search/common - dependencies: - - aff - - aff-promise - - argonaut-core - - arrays - - bifunctors - - codec - - codec-json - - control - - effect - - either - - exceptions - - foldable-traversable - - json - - json-codecs - - language-purescript - - lists - - maybe - - newtype - - ordered-collections - - prelude - - profunctor - - profunctor-lenses - - safe-coerce - - search-trie - - string-parsers - - strings - - transformers - - tuples - - unsafe-coerce - - variant - test_dependencies: [] - build_plan: - - aff - - aff-promise - - argonaut-core - - arrays - - bifunctors - - codec - - codec-json - - console - - const - - contravariant - - control - - datetime - - distributive - - effect - - either - - enums - - exceptions - - exists - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - formatters - - functions - - functors - - gen - - identity - - integers - - invariant - - json - - json-codecs - - language-purescript - - lazy - - lcg - - lists - - maybe - - newtype - - nonempty - - nullable - - numbers - - ordered-collections - - orders - - parallel - - parsing - - partial - - prelude - - profunctor - - profunctor-lenses - - quickcheck - - quickcheck-laws - - random - - record - - refs - - safe-coerce - - search-trie - - st - - string-parsers - - strings - - tailrec - - these - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - variant - - versions + core: + dependencies: + - aff + - aff-promise + - argonaut-core + - arrays + - bifunctors + - codec + - codec-json + - control + - effect + - either + - exceptions + - foldable-traversable + - json + - json-codecs + - language-purescript + - lists + - maybe + - newtype + - ordered-collections + - prelude + - profunctor + - profunctor-lenses + - safe-coerce + - search-trie + - string-parsers + - strings + - transformers + - tuples + - unsafe-coerce + - variant + build_plan: + - aff + - aff-promise + - argonaut-core + - arrays + - bifunctors + - codec + - codec-json + - console + - const + - contravariant + - control + - datetime + - distributive + - effect + - either + - enums + - exceptions + - exists + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - formatters + - functions + - functors + - gen + - identity + - integers + - invariant + - json + - json-codecs + - language-purescript + - lazy + - lcg + - lists + - maybe + - newtype + - nonempty + - nullable + - numbers + - ordered-collections + - orders + - parallel + - parsing + - partial + - prelude + - profunctor + - profunctor-lenses + - quickcheck + - quickcheck-laws + - random + - record + - refs + - safe-coerce + - search-trie + - st + - string-parsers + - strings + - tailrec + - these + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - variant + - versions + test: + dependencies: [] + build_plan: [] docs-search-index: path: docs-search/index - dependencies: - - aff - - argonaut-core - - arrays - - codec-json - - console - - docs-search-common - - effect - - either - - foldable-traversable - - identity - - json - - json-codecs - - language-purescript - - lists - - maybe - - newtype - - node-buffer - - node-fs - - node-path - - node-process - - ordered-collections - - prelude - - profunctor - - search-trie - - string-parsers - - strings - - tuples - - unsafe-coerce - test_dependencies: - - exceptions - - spec - - spec-node - build_plan: - - aff - - aff-promise - - ansi - - argonaut-codecs - - argonaut-core - - arraybuffer-types - - arrays - - avar - - bifunctors - - catenable-lists - - codec - - codec-json - - console - - const - - contravariant - - control - - datetime - - distributive - - docs-search-common - - effect - - either - - enums - - exceptions - - exists - - exitcodes - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - fork - - formatters - - free - - functions - - functors - - gen - - identity - - integers - - invariant - - js-date - - json - - json-codecs - - language-purescript - - lazy - - lcg - - lists - - maybe - - mmorph - - newtype - - node-buffer - - node-event-emitter - - node-fs - - node-path - - node-process - - node-streams - - nonempty - - now - - nullable - - numbers - - open-memoize - - optparse - - ordered-collections - - orders - - parallel - - parsing - - partial - - pipes - - posix-types - - prelude - - profunctor - - profunctor-lenses - - psci-support - - quickcheck - - quickcheck-laws - - random - - record - - refs - - safe-coerce - - search-trie - - spec - - spec-node - - st - - string-parsers - - strings - - tailrec - - these - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - variant - - versions + core: + dependencies: + - aff + - argonaut-core + - arrays + - codec-json + - console + - docs-search-common + - effect + - either + - foldable-traversable + - identity + - json + - json-codecs + - language-purescript + - lists + - maybe + - newtype + - node-buffer + - node-fs + - node-path + - node-process + - ordered-collections + - prelude + - profunctor + - search-trie + - string-parsers + - strings + - tuples + - unsafe-coerce + build_plan: + - aff + - aff-promise + - argonaut-core + - arraybuffer-types + - arrays + - bifunctors + - codec + - codec-json + - console + - const + - contravariant + - control + - datetime + - distributive + - docs-search-common + - effect + - either + - enums + - exceptions + - exists + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - formatters + - functions + - functors + - gen + - identity + - integers + - invariant + - js-date + - json + - json-codecs + - language-purescript + - lazy + - lcg + - lists + - maybe + - newtype + - node-buffer + - node-event-emitter + - node-fs + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - ordered-collections + - orders + - parallel + - parsing + - partial + - posix-types + - prelude + - profunctor + - profunctor-lenses + - quickcheck + - quickcheck-laws + - random + - record + - refs + - safe-coerce + - search-trie + - st + - string-parsers + - strings + - tailrec + - these + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - variant + - versions + test: + dependencies: + - exceptions + - spec + - spec-node + build_plan: + - aff + - ansi + - argonaut-codecs + - argonaut-core + - arraybuffer-types + - arrays + - avar + - bifunctors + - catenable-lists + - console + - const + - contravariant + - control + - datetime + - distributive + - effect + - either + - enums + - exceptions + - exists + - exitcodes + - foldable-traversable + - foreign + - foreign-object + - fork + - free + - functions + - functors + - gen + - identity + - integers + - invariant + - js-date + - lazy + - lists + - maybe + - mmorph + - newtype + - node-buffer + - node-event-emitter + - node-fs + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - open-memoize + - optparse + - ordered-collections + - orders + - parallel + - partial + - pipes + - posix-types + - prelude + - profunctor + - psci-support + - record + - refs + - safe-coerce + - spec + - spec-node + - st + - strings + - tailrec + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unsafe-coerce spago: path: ./ - dependencies: - - aff - - aff-promise - - affjax - - affjax-node - - ansi - - argonaut-core - - arrays - - avar - - codec - - codec-json - - console - - control - - datetime - - docs-search-common - - docs-search-index - - dodo-printer - - effect - - either - - enums - - filterable - - foldable-traversable - - foreign-object - - formatters - - functions - - graphs - - http-methods - - integers - - json - - lists - - maybe - - newtype - - node-buffer - - node-child-process - - node-execa - - node-fs - - node-path - - node-process - - now - - nullable - - ordered-collections - - parallel - - partial - - posix-types - - prelude - - profunctor - - record - - refs - - registry-lib - - routing-duplex - - spago-core - - strings - - strings-extra - - transformers - - tuples - - unfoldable - - unicode - - unsafe-coerce - test_dependencies: - - exceptions - - identity - - quickcheck - - spec - - spec-node - build_plan: - - aff - - aff-promise - - affjax - - affjax-node - - ansi - - argonaut-codecs - - argonaut-core - - arraybuffer-types - - arrays - - assert - - avar - - bifunctors - - catenable-lists - - codec - - codec-json - - console - - const - - contravariant - - control - - datetime - - distributive - - docs-search-common - - docs-search-index - - dodo-printer - - effect - - either - - enums - - exceptions - - exists - - exitcodes - - filterable - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - fork - - form-urlencoded - - formatters - - free - - functions - - functors - - gen - - graphs - - http-methods - - identity - - integers - - invariant - - js-date - - js-timers - - js-uri - - json - - json-codecs - - language-cst-parser - - language-purescript - - lazy - - lcg - - lists - - maybe - - media-types - - mmorph - - newtype - - node-buffer - - node-child-process - - node-event-emitter - - node-execa - - node-fs - - node-human-signals - - node-os - - node-path - - node-process - - node-streams - - nonempty - - now - - nullable - - numbers - - open-memoize - - optparse - - ordered-collections - - orders - - parallel - - parsing - - partial - - pipes - - posix-types - - prelude - - profunctor - - profunctor-lenses - - psci-support - - quickcheck - - quickcheck-laws - - random - - record - - refs - - registry-lib - - routing-duplex - - safe-coerce - - search-trie - - spago-core - - spec - - spec-node - - st - - string-parsers - - strings - - strings-extra - - stringutils - - tailrec - - these - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - unsafe-reference - - variant - - versions - - web-dom - - web-events - - web-file - - web-html - - web-storage - - web-xhr + core: + dependencies: + - aff + - aff-promise + - affjax + - affjax-node + - ansi + - argonaut-core + - arrays + - avar + - codec + - codec-json + - console + - control + - datetime + - docs-search-common + - docs-search-index + - dodo-printer + - effect + - either + - enums + - filterable + - foldable-traversable + - foreign-object + - formatters + - functions + - graphs + - http-methods + - integers + - json + - lists + - maybe + - newtype + - node-buffer + - node-child-process + - node-execa + - node-fs + - node-path + - node-process + - now + - nullable + - ordered-collections + - parallel + - partial + - posix-types + - prelude + - profunctor + - record + - refs + - registry-lib + - routing-duplex + - spago-core + - strings + - strings-extra + - transformers + - tuples + - unfoldable + - unicode + - unsafe-coerce + build_plan: + - aff + - aff-promise + - affjax + - affjax-node + - ansi + - argonaut-core + - arraybuffer-types + - arrays + - assert + - avar + - bifunctors + - catenable-lists + - codec + - codec-json + - console + - const + - contravariant + - control + - datetime + - distributive + - docs-search-common + - docs-search-index + - dodo-printer + - effect + - either + - enums + - exceptions + - exists + - filterable + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - form-urlencoded + - formatters + - free + - functions + - functors + - gen + - graphs + - http-methods + - identity + - integers + - invariant + - js-date + - js-timers + - js-uri + - json + - json-codecs + - language-cst-parser + - language-purescript + - lazy + - lcg + - lists + - maybe + - media-types + - newtype + - node-buffer + - node-child-process + - node-event-emitter + - node-execa + - node-fs + - node-human-signals + - node-os + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - ordered-collections + - orders + - parallel + - parsing + - partial + - posix-types + - prelude + - profunctor + - profunctor-lenses + - quickcheck + - quickcheck-laws + - random + - record + - refs + - registry-lib + - routing-duplex + - safe-coerce + - search-trie + - spago-core + - st + - string-parsers + - strings + - strings-extra + - stringutils + - tailrec + - these + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - unsafe-reference + - variant + - versions + - web-dom + - web-events + - web-file + - web-html + - web-storage + - web-xhr + test: + dependencies: + - exceptions + - identity + - quickcheck + - spec + - spec-node + build_plan: + - aff + - ansi + - argonaut-codecs + - argonaut-core + - arraybuffer-types + - arrays + - avar + - bifunctors + - catenable-lists + - console + - const + - contravariant + - control + - datetime + - distributive + - effect + - either + - enums + - exceptions + - exists + - exitcodes + - foldable-traversable + - foreign + - foreign-object + - fork + - free + - functions + - functors + - gen + - identity + - integers + - invariant + - js-date + - lazy + - lcg + - lists + - maybe + - mmorph + - newtype + - node-buffer + - node-event-emitter + - node-fs + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - open-memoize + - optparse + - ordered-collections + - orders + - parallel + - partial + - pipes + - posix-types + - prelude + - profunctor + - psci-support + - quickcheck + - random + - record + - refs + - safe-coerce + - spec + - spec-node + - st + - strings + - tailrec + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unsafe-coerce spago-bin: path: bin - dependencies: - - aff - - arrays - - avar - - codec-json - - control - - foldable-traversable - - lists - - maybe - - node-path - - node-process - - now - - optparse - - ordered-collections - - prelude - - record - - registry-lib - - spago - - spago-core - - strings - - transformers - - unsafe-coerce - test_dependencies: [] - build_plan: - - aff - - aff-promise - - affjax - - affjax-node - - ansi - - argonaut-codecs - - argonaut-core - - arraybuffer-types - - arrays - - assert - - avar - - bifunctors - - catenable-lists - - codec - - codec-json - - console - - const - - contravariant - - control - - datetime - - distributive - - docs-search-common - - docs-search-index - - dodo-printer - - effect - - either - - enums - - exceptions - - exists - - exitcodes - - filterable - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - fork - - form-urlencoded - - formatters - - free - - functions - - functors - - gen - - graphs - - http-methods - - identity - - integers - - invariant - - js-date - - js-timers - - js-uri - - json - - json-codecs - - language-cst-parser - - language-purescript - - lazy - - lcg - - lists - - maybe - - media-types - - mmorph - - newtype - - node-buffer - - node-child-process - - node-event-emitter - - node-execa - - node-fs - - node-human-signals - - node-os - - node-path - - node-process - - node-streams - - nonempty - - now - - nullable - - numbers - - open-memoize - - optparse - - ordered-collections - - orders - - parallel - - parsing - - partial - - pipes - - posix-types - - prelude - - profunctor - - profunctor-lenses - - psci-support - - quickcheck - - quickcheck-laws - - random - - record - - refs - - registry-lib - - routing-duplex - - safe-coerce - - search-trie - - spago - - spago-core - - spec - - spec-node - - st - - string-parsers - - strings - - strings-extra - - stringutils - - tailrec - - these - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - unsafe-reference - - variant - - versions - - web-dom - - web-events - - web-file - - web-html - - web-storage - - web-xhr + core: + dependencies: + - aff + - arrays + - avar + - codec-json + - control + - foldable-traversable + - lists + - maybe + - node-path + - node-process + - now + - optparse + - ordered-collections + - prelude + - record + - registry-lib + - spago + - spago-core + - strings + - transformers + - unsafe-coerce + build_plan: + - aff + - aff-promise + - affjax + - affjax-node + - ansi + - argonaut-core + - arraybuffer-types + - arrays + - assert + - avar + - bifunctors + - catenable-lists + - codec + - codec-json + - console + - const + - contravariant + - control + - datetime + - distributive + - docs-search-common + - docs-search-index + - dodo-printer + - effect + - either + - enums + - exceptions + - exists + - exitcodes + - filterable + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - form-urlencoded + - formatters + - free + - functions + - functors + - gen + - graphs + - http-methods + - identity + - integers + - invariant + - js-date + - js-timers + - js-uri + - json + - json-codecs + - language-cst-parser + - language-purescript + - lazy + - lcg + - lists + - maybe + - media-types + - newtype + - node-buffer + - node-child-process + - node-event-emitter + - node-execa + - node-fs + - node-human-signals + - node-os + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - open-memoize + - optparse + - ordered-collections + - orders + - parallel + - parsing + - partial + - posix-types + - prelude + - profunctor + - profunctor-lenses + - psci-support + - quickcheck + - quickcheck-laws + - random + - record + - refs + - registry-lib + - routing-duplex + - safe-coerce + - search-trie + - spago + - spago-core + - st + - string-parsers + - strings + - strings-extra + - stringutils + - tailrec + - these + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - unsafe-reference + - variant + - versions + - web-dom + - web-events + - web-file + - web-html + - web-storage + - web-xhr + test: + dependencies: [] + build_plan: [] spago-core: path: core - dependencies: - - aff - - arrays - - bifunctors - - codec - - codec-json - - console - - control - - datetime - - dodo-printer - - effect - - either - - exceptions - - filterable - - foldable-traversable - - functions - - identity - - integers - - json - - lists - - maybe - - newtype - - node-buffer - - node-fs - - node-path - - node-process - - now - - ordered-collections - - partial - - prelude - - profunctor - - refs - - registry-lib - - strings - - stringutils - - transformers - - tuples - test_dependencies: [] - build_plan: - - aff - - ansi - - arraybuffer-types - - arrays - - assert - - avar - - bifunctors - - catenable-lists - - codec - - codec-json - - console - - const - - contravariant - - control - - datetime - - distributive - - dodo-printer - - effect - - either - - enums - - exceptions - - exists - - filterable - - fixed-points - - foldable-traversable - - foreign - - foreign-object - - formatters - - free - - functions - - functors - - gen - - graphs - - identity - - integers - - invariant - - js-date - - js-uri - - json - - language-cst-parser - - lazy - - lcg - - lists - - maybe - - newtype - - node-buffer - - node-event-emitter - - node-fs - - node-path - - node-process - - node-streams - - nonempty - - now - - nullable - - numbers - - ordered-collections - - orders - - parallel - - parsing - - partial - - posix-types - - prelude - - profunctor - - profunctor-lenses - - quickcheck - - random - - record - - refs - - registry-lib - - routing-duplex - - safe-coerce - - st - - strings - - stringutils - - tailrec - - transformers - - tuples - - type-equality - - typelevel-prelude - - unfoldable - - unicode - - unsafe-coerce - - variant + core: + dependencies: + - aff + - arrays + - bifunctors + - codec + - codec-json + - console + - control + - datetime + - dodo-printer + - effect + - either + - exceptions + - filterable + - foldable-traversable + - functions + - identity + - integers + - json + - lists + - maybe + - newtype + - node-buffer + - node-fs + - node-path + - node-process + - now + - ordered-collections + - partial + - prelude + - profunctor + - refs + - registry-lib + - strings + - stringutils + - transformers + - tuples + build_plan: + - aff + - ansi + - arraybuffer-types + - arrays + - assert + - avar + - bifunctors + - catenable-lists + - codec + - codec-json + - console + - const + - contravariant + - control + - datetime + - distributive + - dodo-printer + - effect + - either + - enums + - exceptions + - exists + - filterable + - fixed-points + - foldable-traversable + - foreign + - foreign-object + - formatters + - free + - functions + - functors + - gen + - graphs + - identity + - integers + - invariant + - js-date + - js-uri + - json + - language-cst-parser + - lazy + - lcg + - lists + - maybe + - newtype + - node-buffer + - node-event-emitter + - node-fs + - node-path + - node-process + - node-streams + - nonempty + - now + - nullable + - numbers + - ordered-collections + - orders + - parallel + - parsing + - partial + - posix-types + - prelude + - profunctor + - profunctor-lenses + - quickcheck + - random + - record + - refs + - registry-lib + - routing-duplex + - safe-coerce + - st + - strings + - stringutils + - tailrec + - transformers + - tuples + - type-equality + - typelevel-prelude + - unfoldable + - unicode + - unsafe-coerce + - variant + test: + dependencies: [] + build_plan: [] package_set: address: registry: 56.4.0 diff --git a/src/Spago/Command/Build.purs b/src/Spago/Command/Build.purs index 53f75a8f0..9bdabea50 100644 --- a/src/Spago/Command/Build.purs +++ b/src/Spago/Command/Build.purs @@ -43,7 +43,7 @@ type BuildOptions = , jsonErrors :: Boolean } -run :: BuildOptions -> Spago (BuildEnv _) Boolean +run :: ∀ a. BuildOptions -> Spago (BuildEnv a) Boolean run opts = do logInfo "Building..." { dependencies @@ -104,8 +104,13 @@ run opts = do Nothing -> Config.getWorkspacePackages workspace.packageSet globs = getBuildGlobs { dependencies: case workspace.selected of - Just p -> unsafeFromJust $ Map.lookup p.package.name dependencies - Nothing -> allDependencies + Just p -> + let + { core, test } = unsafeFromJust $ Map.lookup p.package.name dependencies + in + Map.union core test + Nothing -> + allDependencies , depsOnly: opts.depsOnly , withTests: true , selected: selectedPackages @@ -202,7 +207,7 @@ getBuildGlobs { selected, dependencies, withTests, depsOnly } = dependencyGlobs = (Tuple.uncurry $ Config.sourceGlob NoTestGlobs) =<< dependencyPkgs monorepoPkgGlobs | depsOnly = [] - | otherwise = (Tuple.uncurry $ Config.sourceGlob testGlobs) =<< monorepoPkgs + | otherwise = (Tuple.uncurry $ Config.sourceGlob NoTestGlobs) =<< monorepoPkgs isWorkspacePackage :: Tuple PackageName Package -> Boolean isWorkspacePackage = case _ of diff --git a/src/Spago/Command/Fetch.purs b/src/Spago/Command/Fetch.purs index 4a1febfcd..8c249404d 100644 --- a/src/Spago/Command/Fetch.purs +++ b/src/Spago/Command/Fetch.purs @@ -1,13 +1,14 @@ module Spago.Command.Fetch - ( PackageTransitiveDeps + ( ByEnv , FetchEnv , FetchEnvRow , FetchOpts - , toAllDependencies - , getWorkspacePackageDeps + , PackageTransitiveDeps , getTransitiveDeps , getTransitiveDepsFromRegistry + , getWorkspacePackageDeps , run + , toAllDependencies , writeNewLockfile ) where @@ -22,10 +23,9 @@ import Data.Array.NonEmpty as NEA import Data.Codec.JSON as CJ import Data.Codec.JSON.Common as CJ.Common import Data.Either as Either -import Data.Filterable (filterMap) +import Data.FunctorWithIndex (mapWithIndex) import Data.HTTP.Method as Method import Data.Int as Int -import Data.List as List import Data.Map as Map import Data.Newtype (wrap) import Data.Set as Set @@ -57,7 +57,7 @@ import Spago.Registry as Registry import Spago.Repl as Repl import Spago.Tar as Tar -type PackageTransitiveDeps = Map PackageName PackageMap +type PackageTransitiveDeps = Map PackageName (ByEnv PackageMap) type FetchEnvRow a = ( getRegistry :: Spago (Registry.PreRegistryEnv ()) Registry.RegistryFunctions @@ -87,19 +87,21 @@ run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do let getPackageConfigPath errorMessageEnd = do - case currentWorkspace.selected of - Just { path, doc, package } -> pure { configPath: Path.concat [ path, "spago.yaml" ], yamlDoc: doc, package } - Nothing -> case currentWorkspace.rootPackage of - Just rootPackage -> do pure { configPath: "spago.yaml", yamlDoc: currentWorkspace.doc, package: rootPackage } - Nothing -> die - [ "No package found in the root configuration." - , "Please use the `-p` flag to select a package " <> errorMessageEnd - ] + case currentWorkspace.selected, currentWorkspace.rootPackage of + Just { path, doc, package }, _ -> + pure { configPath: Path.concat [ path, "spago.yaml" ], yamlDoc: doc, package } + _, Just rootPackage -> + pure { configPath: "spago.yaml", yamlDoc: currentWorkspace.doc, package: rootPackage } + Nothing, Nothing -> die + [ "No package found in the root configuration." + , "Please use the `-p` flag to select a package " <> errorMessageEnd + ] installingPackagesData <- do - case Array.null packagesRequestedToInstall of - true -> pure Nothing - false -> do + case packagesRequestedToInstall of + [] -> + pure Nothing + _ -> do { configPath, package, yamlDoc } <- getPackageConfigPath "to install your packages in." currentWorkspacePackage <- NEA.find (\p -> p.package.name == package.name) (Config.getWorkspacePackages currentWorkspace.packageSet) `justOrDieWith` "Impossible: package must be in workspace packages" let @@ -152,23 +154,21 @@ run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do $ map (\p -> Tuple p.package.name p) $ Config.getWorkspacePackages workspace.packageSet - case installingPackagesData of - Nothing -> pure unit - Just { configPath, yamlDoc, actualPackagesToInstall } -> do - let - countString = case Array.length actualPackagesToInstall of - 1 -> "1 package" - n -> show n <> " packages" - logInfo $ "Adding " <> countString <> " to the config in " <> configPath - liftAff $ Config.addPackagesToConfig configPath yamlDoc isTest actualPackagesToInstall + for_ installingPackagesData \{ configPath, yamlDoc, actualPackagesToInstall } -> do + let + countString = case Array.length actualPackagesToInstall of + 1 -> "1 package" + n -> show n <> " packages" + logInfo $ "Adding " <> countString <> " to the config in " <> configPath + liftAff $ Config.addPackagesToConfig configPath yamlDoc isTest actualPackagesToInstall -- if the flag is selected, we kick off the process of adding ranges to the config when ensureRanges do { configPath, package, yamlDoc } <- getPackageConfigPath "in which to add ranges." - logInfo $ "Adding ranges to dependencies to the config in " <> configPath + logInfo $ "Adding ranges to core dependencies to the config in " <> configPath packageDeps <- (Map.lookup package.name dependencies) `justOrDieWith` "Impossible: package dependencies must be in dependencies map" - let rangeMap = map getRangeFromPackage packageDeps + let rangeMap = map getRangeFromPackage packageDeps.core liftEffect $ Config.addRangesToConfig yamlDoc rangeMap liftAff $ FS.writeYamlDocFile configPath yamlDoc @@ -177,13 +177,18 @@ run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do let allTransitiveDeps = case isRepl of false -> dependencies - true -> map (\packageMap -> Map.union packageMap supportPackage) dependencies + -- It should be enough to add the support package to the core dependencies only, + -- but that's more code, so nevermind + true -> map (onEachEnv \packageMap -> Map.union packageMap supportPackage) dependencies depsToFetch <- case workspace.selected of Nothing -> pure (toAllDependencies allTransitiveDeps) -- If there's a package selected, we only fetch the transitive deps for that one Just p -> case Map.lookup p.package.name dependencies of - Nothing -> die "Impossible: package dependencies must be in dependencies map" - Just deps -> pure $ Map.union deps if isRepl then supportPackage else Map.empty + Nothing -> + die "Impossible: package dependencies must be in dependencies map" + Just deps -> do + let supportDeps = if isRepl then supportPackage else Map.empty + pure $ Map.union supportDeps $ Map.union deps.test deps.core -- then for every package we have we try to download it, and copy it in the local cache logInfo "Downloading dependencies..." @@ -276,18 +281,23 @@ run { packages: packagesRequestedToInstall, ensureRanges, isTest, isRepl } = do Right _lockfile -> pure dependencies Left reason -> writeNewLockfile reason dependencies -type LockfileBuilderResult = - { workspacePackages :: Map PackageName Lock.WorkspaceLockPackage - , packages :: Map PackageName Lock.LockEntry - } - -lookupInCache :: forall a k v. Ord k => k -> Ref.Ref (Map k v) -> Spago a (Maybe v) +lookupInCache :: ∀ a k v. Ord k => k -> Ref.Ref (Map k v) -> Spago a (Maybe v) lookupInCache key cacheRef = liftEffect $ Ref.read cacheRef >>= Map.lookup key >>> pure -updateCache :: forall a k v. Ord k => k -> v -> Ref.Ref (Map k v) -> Spago a Unit +withCache :: ∀ a k v. Ord k => k -> Ref.Ref (Map k v) -> Spago a v -> Spago a v +withCache key cacheRef create = + lookupInCache key cacheRef >>= case _ of + Just v -> + pure v + Nothing -> do + v <- create + updateCache key v cacheRef + pure v + +updateCache :: ∀ a k v. Ord k => k -> v -> Ref.Ref (Map k v) -> Spago a Unit updateCache key value cacheRef = liftEffect $ Ref.modify_ (Map.insert key value) cacheRef -writeNewLockfile :: forall a. String -> PackageTransitiveDeps -> Spago (FetchEnv a) PackageTransitiveDeps +writeNewLockfile :: ∀ a. String -> PackageTransitiveDeps -> Spago (FetchEnv a) PackageTransitiveDeps writeNewLockfile reason allTransitiveDeps = do logInfo $ reason <> ", generating it..." { workspace } <- ask @@ -296,126 +306,99 @@ writeNewLockfile reason allTransitiveDeps = do packageDependenciesCache <- liftEffect $ Ref.new Map.empty gitRefCache <- liftEffect $ Ref.new Map.empty metadataRefCache <- liftEffect $ Ref.new Map.empty - let - memoisedGetPackageDependencies :: PackageName -> Package -> Spago (FetchEnv a) (Maybe (Map PackageName Range)) - memoisedGetPackageDependencies packageName package = do - lookupInCache packageName packageDependenciesCache >>= - case _ of - Just cached -> do - pure cached - Nothing -> do - -- Not cached. Compute it, write to ref, return it - res <- getPackageDependencies packageName package - updateCache packageName res packageDependenciesCache - pure res - - processPackage :: Map PackageName _ -> LockfileBuilderResult -> Tuple PackageName (Tuple PackageName Package) -> Spago (FetchEnv a) LockfileBuilderResult - processPackage registryIntegrityMap result (Tuple workspacePackageName (Tuple dependencyName dependencyPackage)) = do - let - getDeps = (Array.fromFoldable <<< Map.keys <<< fromMaybe Map.empty) - <$> memoisedGetPackageDependencies dependencyName dependencyPackage - - updatePackage r package = (updateWorkspacePackage r) - { packages = Map.insert dependencyName package r.packages } - updateWorkspacePackage r = r - { workspacePackages = Map.alter - ( case _ of - Nothing -> Nothing - Just pkg -> Just $ pkg { build_plan = Set.insert dependencyName (pkg.build_plan) } - ) - workspacePackageName - r.workspacePackages - } - - case dependencyPackage of - WorkspacePackage _pkg -> pure $ updateWorkspacePackage result - - GitPackage gitPackage -> do - let packageLocation = Config.getPackageLocation dependencyName dependencyPackage - lookupInCache packageLocation gitRefCache >>= case _ of - Nothing -> - -- Get the ref and update the cache - Git.getRef (Just packageLocation) >>= case _ of - Left err -> die err -- TODO maybe not die here? - Right rev -> do - dependencies <- getDeps - let - lockEntry = - FromGit { rev, dependencies, url: gitPackage.git, subdir: gitPackage.subdir } - updateCache packageLocation lockEntry gitRefCache - pure $ updatePackage result lockEntry - Just entry -> pure $ updatePackage result entry - - RegistryVersion version -> do - lookupInCache dependencyName metadataRefCache >>= case _ of - Nothing -> do - registryVersion <- FromRegistry <$> case Map.lookup dependencyName registryIntegrityMap of - -- This shouldn't be Nothing because it's already handled when building the integrity map below - Nothing -> die $ "Couldn't read metadata" - Just integrity -> do - dependencies <- getDeps - pure { version, integrity, dependencies } - updateCache dependencyName registryVersion metadataRefCache - pure $ updatePackage result registryVersion - Just entry -> do - pure $ updatePackage result entry - - LocalPackage { path } -> do - dependencies <- getDeps - pure $ updatePackage result $ FromPath { path, dependencies } - - let - toArray :: forall k v. Map k v -> Array (Tuple k v) - toArray = Map.toUnfoldable - allDependencies = foldMap sequence $ toArray $ map toArray allTransitiveDeps -- Fetch the Registry metadata in one go for all required packages let - uniqueRegistryPackageNames = Array.nub $ filterMap - ( \(Tuple _ (Tuple dependencyName dependencyPackage)) -> case dependencyPackage of - RegistryVersion _ -> Just dependencyName - _ -> Nothing - ) - allDependencies + allDependencies = toAllDependencies allTransitiveDeps + + uniqueRegistryPackageNames = do + name /\ package <- Map.toUnfoldable allDependencies + case package of + RegistryVersion _ -> pure name + _ -> [] + metadataMap <- Registry.getMetadataForPackages uniqueRegistryPackageNames >>= case _ of Left err -> die $ "Couldn't read metadata, reason:\n " <> err Right ms -> pure ms - (registryVersions :: Map PackageName Sha256) <- Map.fromFoldable <<< Array.catMaybes <$> - ( traverse - ( \(Tuple _ (Tuple dependencyName dependencyPackage)) -> case dependencyPackage of - RegistryVersion version -> do - let metadata = Map.lookup dependencyName metadataMap - case (metadata >>= (\(Metadata meta) -> Map.lookup version meta.published)) of - Nothing | isNothing metadata -> - die $ "Couldn't read metadata for " <> PackageName.print dependencyName - Nothing -> - die $ "Couldn't read metadata for " <> PackageName.print dependencyName - <> ": didn't find version in the metadata file" - Just { hash: integrity } -> - pure $ Just $ dependencyName /\ integrity - _ -> pure Nothing - ) - $ allDependencies - ) - - ({ packages, workspacePackages } :: LockfileBuilderResult) <- - -- NOTE! We used to have `Array.foldM` here, but it was significantly slower - -- (~10ms vs 6s on a very large project) - List.foldM (processPackage registryVersions) - { workspacePackages: Map.fromFoldable $ map Config.workspacePackageToLockfilePackage (Config.getWorkspacePackages workspace.packageSet) - , packages: Map.empty - } - $ List.fromFoldable allDependencies + registryVersions :: Map PackageName Sha256 <- sequence do + allDependencies # Map.mapMaybeWithKey \name package -> + case package of + RegistryVersion version -> Just do + let metadata = Map.lookup name metadataMap + case (metadata >>= (\(Metadata meta) -> Map.lookup version meta.published)) of + Nothing | isNothing metadata -> + die $ "Couldn't read metadata for " <> PackageName.print name + Nothing -> + die $ "Couldn't read metadata for " <> PackageName.print name + <> ": didn't find version in the metadata file" + Just { hash: integrity } -> + pure integrity + _ -> + Nothing + + let + -- Used only as part of `packageToLockEntry`, this function returns only + -- core dependencies of the given package, not test ones. This is because + -- `packageToLockEntry` is used only for non-workspace packages (i.e. true + -- dependencies of the whole workspace), for which we do not care about test + -- dependencies. + corePackageDepsOrEmpty packageName package = + withCache packageName packageDependenciesCache do + getPackageDependencies packageName package <#> case _ of + Just deps -> Array.fromFoldable $ Map.keys deps.core + Nothing -> [] + + packageToLockEntry packageName package = case package of + WorkspacePackage _ -> + Nothing + GitPackage gitPackage -> Just do + let packageLocation = Config.getPackageLocation packageName package + withCache packageLocation gitRefCache do + Git.getRef (Just packageLocation) >>= case _ of + Left err -> + die err -- TODO maybe not die here? + Right rev -> do + dependencies <- corePackageDepsOrEmpty packageName package + pure $ FromGit { rev, dependencies, url: gitPackage.git, subdir: gitPackage.subdir } + RegistryVersion version -> Just do + withCache packageName metadataRefCache do + FromRegistry <$> case Map.lookup packageName registryVersions of + -- This shouldn't be Nothing because it's already handled when building the integrity map above + Nothing -> + die $ "Couldn't read metadata" + Just integrity -> do + dependencies <- corePackageDepsOrEmpty packageName package + pure { version, integrity, dependencies } + LocalPackage { path } -> Just do + dependencies <- corePackageDepsOrEmpty packageName package + pure $ FromPath { path, dependencies } + + -- For every package that is a `WorkspacePackage`, we just pick up all + -- transitive core and test dependencies of that package from + -- `allTransitiveDeps` and stick them into `core { build_plan }` and + -- `test { build_plan }` respectively. + workspacePackageLockEntries = Map.fromFoldable do + name /\ package <- Config.workspacePackageToLockfilePackage <$> Config.getWorkspacePackages workspace.packageSet + let deps = allTransitiveDeps # Map.lookup name # fromMaybe { core: Map.empty, test: Map.empty } + pure $ name /\ package + { core { build_plan = Map.keys deps.core } + , test { build_plan = Map.keys deps.test } + } + + -- For every non-workspace package, we convert it to its respective type of + -- lockfile entry via `packageToLockEntry`. + nonWorkspacePackageLockEntries <- + sequence $ Map.mapMaybeWithKey packageToLockEntry allDependencies let lockfile = - { packages + { packages: nonWorkspacePackageLockEntries , workspace: { package_set: case workspace.packageSet.buildType of RegistrySolverBuild _ -> Nothing PackageSetBuild info _ -> Just info - , packages: workspacePackages + , packages: workspacePackageLockEntries , extra_packages: fromMaybe Map.empty workspace.workspaceConfig.extraPackages } } @@ -424,21 +407,25 @@ writeNewLockfile reason allTransitiveDeps = do -- We update the dependencies here with the commit hashes that came from the getRef calls, -- so that the build uses them instead of the tags - pure $ Map.mapMaybeWithKey - ( \_workspacePackage packageMap -> Just $ Map.mapMaybeWithKey - ( \name package -> Just case package of - GitPackage gitPackage -> case Map.lookup name packages of - Nothing -> package - Just (FromGit { rev }) -> GitPackage $ gitPackage { ref = rev } - Just _ -> package - _ -> package - ) - packageMap - ) - allTransitiveDeps - + let + updateGitDependencyRefsToCommitHashes = + mapWithIndex \name package -> case package of + GitPackage gitPackage -> case Map.lookup name nonWorkspacePackageLockEntries of + Nothing -> package + Just (FromGit { rev }) -> GitPackage $ gitPackage { ref = rev } + Just _ -> package + _ -> + package + + pure $ allTransitiveDeps <#> onEachEnv updateGitDependencyRefsToCommitHashes + +-- | Given a map of several packages to their respective dependencies, collapses +-- | all dependencies into a single bucket. toAllDependencies :: PackageTransitiveDeps -> PackageMap -toAllDependencies = foldl (Map.unionWith (\l _ -> l)) Map.empty +toAllDependencies = + Map.values + >>> foldMap (\m -> [ m.core, m.test ]) + >>> foldl Map.union Map.empty getGitPackageInLocalCache :: forall a. PackageName -> GitPackage -> Spago (Git.GitEnv a) Unit getGitPackageInLocalCache name package = do @@ -466,11 +453,11 @@ getGitPackageInLocalCache name package = do FS.mkdirp $ Path.concat [ Paths.localCachePackagesPath, PackageName.print name ] FS.copyTree { src: localPackageLocation, dst: commitHashLocation } -getPackageDependencies :: forall a. PackageName -> Package -> Spago (FetchEnv a) (Maybe (Map PackageName Range)) +getPackageDependencies :: forall a. PackageName -> Package -> Spago (FetchEnv a) (Maybe (ByEnv (Map PackageName Range))) getPackageDependencies packageName package = case package of RegistryVersion v -> do maybeManifest <- Registry.getManifestFromIndex packageName v - pure $ map (_.dependencies <<< unwrap) maybeManifest + pure $ maybeManifest <#> \(Manifest m) -> { core: m.dependencies, test: Map.empty } GitPackage p -> do -- Note: we get the package in local cache nonetheless, -- so we have guarantees about being able to fetch it @@ -478,7 +465,8 @@ getPackageDependencies packageName package = case package of unlessM (FS.exists packageLocation) do getGitPackageInLocalCache packageName p case p.dependencies of - Just (Dependencies dependencies) -> pure (Just (map (fromMaybe Config.widestRange) dependencies)) + Just (Dependencies dependencies) -> + pure $ Just { core: map (fromMaybe Config.widestRange) dependencies, test: Map.empty } Nothing -> do readLocalDependencies case p.subdir of Nothing -> packageLocation @@ -486,15 +474,18 @@ getPackageDependencies packageName package = case package of LocalPackage p -> do readLocalDependencies p.path WorkspacePackage p -> - pure (Just (map (fromMaybe Config.widestRange) (unwrap $ getWorkspacePackageDeps p))) + pure $ Just $ (map (fromMaybe Config.widestRange) <<< unwrap) `onEachEnv` getWorkspacePackageDeps p where -- try to see if the package has a spago config, and if it's there we read it - readLocalDependencies :: FilePath -> Spago (FetchEnv a) (Maybe (Map PackageName Range)) + readLocalDependencies :: FilePath -> Spago (FetchEnv a) (Maybe (ByEnv (Map PackageName Range))) readLocalDependencies configLocation = do -- TODO: make this work with manifests Config.readConfig (Path.concat [ configLocation, "spago.yaml" ]) >>= case _ of - Right { yaml: { package: Just { dependencies: (Dependencies deps) } } } -> do - pure (Just (map (fromMaybe Config.widestRange) deps)) + Right { yaml: { package: Just { dependencies: Dependencies deps, test } } } -> + pure $ Just + { core: fromMaybe Config.widestRange <$> deps + , test: fromMaybe Config.widestRange <$> (test <#> _.dependencies <#> unwrap # fromMaybe Map.empty) + } Right _ -> die [ "Read the configuration at path " <> configLocation , "However, it didn't contain a `package` section." @@ -505,11 +496,12 @@ getPackageDependencies packageName package = case package of , indent $ toDoc errLines ] -getWorkspacePackageDeps :: WorkspacePackage -> Dependencies +getWorkspacePackageDeps :: WorkspacePackage -> ByEnv Dependencies getWorkspacePackageDeps pkg = - if pkg.hasTests then - pkg.package.dependencies <> fromMaybe mempty (map _.dependencies pkg.package.test) - else pkg.package.dependencies + { core: pkg.package.dependencies + , test: fromMaybe (Dependencies Map.empty) $ + if pkg.hasTests then _.dependencies <$> pkg.package.test else Nothing + } type TransitiveDepsResult = { packages :: Map PackageName Package @@ -520,38 +512,72 @@ type TransitiveDepsResult = } } -getTransitiveDeps :: forall a. Config.WorkspacePackage -> Spago (FetchEnv a) PackageMap +-- | For a given workspace package, returns a list of all its transitive +-- | dependencies, but seperately for core and test. +-- | +-- | Note that test dependencies are _not transitive_. That is, if package A's +-- | tests depend on package B, it means that package A's tests transitively +-- | depend on package B's core dependencies, not test ones. Package B's test +-- | dependencies are used only for package B's own tests, not for tests of any +-- | of its consumers. +-- | +-- | For this reason, this function only picks up the core and test dependencies +-- | of the root package, and after that computes the transitive dependencies of +-- | each by traversing core dependencies of the dependencies. +-- | +-- | The traversal itself is done in either `getTransitiveDepsFromRegistry` or +-- | `getTransitiveDepsFromPackageSet`, depending on what package source the +-- | workspace is using. +getTransitiveDeps :: forall a. Config.WorkspacePackage -> Spago (FetchEnv a) (ByEnv PackageMap) getTransitiveDeps workspacePackage = do - let depsRanges = map (fromMaybe Config.widestRange) (unwrap $ getWorkspacePackageDeps workspacePackage) + let depsRanges = (map (fromMaybe Config.widestRange) <<< unwrap) `onEachEnv` getWorkspacePackageDeps workspacePackage { workspace } <- ask case workspace.packageSet.lockfile of -- If we have a lockfile we can just use that - we don't need build a plan, since we store it for every workspace -- package, so we can just filter out the packages we need. Right lockfile -> do case Map.lookup workspacePackage.package.name lockfile.workspace.packages of - Nothing -> die $ "Package " <> PackageName.print workspacePackage.package.name <> " not found in lockfile" - Just { build_plan } -> do - let + Nothing -> + die $ "Package " <> PackageName.print workspacePackage.package.name <> " not found in lockfile" + Just envs -> + pure + { core: fromBuildPlan envs.core.build_plan + , test: fromBuildPlan envs.test.build_plan + } + where + fromBuildPlan bp = Map.union otherPackages workspacePackagesWeNeed + where allWorkspacePackages = Map.fromFoldable $ map (\p -> Tuple p.package.name (WorkspacePackage p)) (Config.getWorkspacePackages workspace.packageSet) isInBuildPlan :: forall v. PackageName -> v -> Boolean - isInBuildPlan name _package = Set.member name build_plan + isInBuildPlan name _package = Set.member name bp workspacePackagesWeNeed = Map.filterWithKey isInBuildPlan allWorkspacePackages otherPackages = map fromLockEntry $ Map.filterWithKey isInBuildPlan lockfile.packages - pure $ Map.union otherPackages workspacePackagesWeNeed - -- No lockfile, we need to build a plan from scratch, and hit the Registry and so on Left _ -> case workspace.packageSet.buildType of RegistrySolverBuild extraPackages -> do - plan <- getTransitiveDepsFromRegistry depsRanges extraPackages - logDebug $ "Got a plan from the Solver: " <> printJson (Internal.Codec.packageMap Version.codec) plan - pure $ plan # Map.mapMaybeWithKey \packageName version -> case Map.lookup packageName extraPackages of - Just p -> Just p - Nothing -> Just $ RegistryVersion version - - PackageSetBuild _info set -> getTransitiveDepsFromPackageSet set (Array.fromFoldable $ Map.keys depsRanges) + let + forEnv envName depsRanges' = do + plan <- getTransitiveDepsFromRegistry depsRanges' extraPackages + logDebug $ Array.fold + [ "Got a plan from the Solver for " + , envName + , " deps: " + , printJson (Internal.Codec.packageMap Version.codec) plan + ] + pure $ plan # Map.mapMaybeWithKey \packageName version -> case Map.lookup packageName extraPackages of + Just p -> Just p + Nothing -> Just $ RegistryVersion version + + { core: _, test: _ } + <$> forEnv "core" depsRanges.core + <*> forEnv "test" depsRanges.test + + PackageSetBuild _info set -> do + depsRanges # onEachEnvM \depsRanges' -> + getTransitiveDepsFromPackageSet set $ (Array.fromFoldable $ Map.keys depsRanges') where -- Note: here we can safely discard the dependencies because we don't need to bother about building a build plan, @@ -568,6 +594,7 @@ getTransitiveDeps workspacePackage = do , subdir } +-- | See comments on `getTransitiveDeps`. getTransitiveDepsFromRegistry :: forall a. Map PackageName Range -> PackageMap -> Spago (FetchEnv a) (Map PackageName Version) getTransitiveDepsFromRegistry depsRanges extraPackages = do let @@ -575,7 +602,10 @@ getTransitiveDepsFromRegistry depsRanges extraPackages = do loader packageName = do -- First look up in the extra packages, as they are the workspace ones, and overrides case Map.lookup packageName extraPackages of - Just p -> map (Map.singleton (getVersionFromPackage p) <<< fromMaybe Map.empty) $ getPackageDependencies packageName p + Just p -> do + deps <- getPackageDependencies packageName p + let coreDeps = deps <#> _.core # fromMaybe Map.empty + pure $ Map.singleton (getVersionFromPackage p) coreDeps Nothing -> do maybeMetadata <- Registry.getMetadata packageName let @@ -586,17 +616,19 @@ getTransitiveDepsFromRegistry depsRanges extraPackages = do maybeManifest <- Registry.getManifestFromIndex packageName v let deps = fromMaybe Map.empty $ map (_.dependencies <<< unwrap) maybeManifest pure (Tuple v deps) + maybePlan <- Registry.Solver.loadAndSolve loader depsRanges + case maybePlan of Left errs -> die [ toDoc "Could not solve the package dependencies, errors:" , indent $ toDoc $ Array.fromFoldable $ map Registry.Solver.printSolverError errs ] - Right (buildPlan :: Map PackageName Version) -> do + Right buildPlan -> pure buildPlan --- | Return the transitive dependencies of a list of packages -getTransitiveDepsFromPackageSet :: forall a. PackageMap -> Array PackageName -> Spago (FetchEnv a) PackageMap +-- | See comments on `getTransitiveDeps`. +getTransitiveDepsFromPackageSet :: ∀ a. PackageMap -> Array PackageName -> Spago (FetchEnv a) PackageMap getTransitiveDepsFromPackageSet packageSet deps = do logDebug "Getting transitive deps" packageDependenciesCache <- liftEffect $ Ref.new Map.empty @@ -608,9 +640,9 @@ getTransitiveDepsFromPackageSet packageSet deps = do Just cached -> pure cached Nothing -> do -- Not cached. Compute it, write to ref, return it - res <- getPackageDependencies packageName package - liftEffect $ Ref.modify_ (Map.insert packageName res) packageDependenciesCache - pure res + coreDeps <- map _.core <$> getPackageDependencies packageName package + liftEffect $ Ref.modify_ (Map.insert packageName coreDeps) packageDependenciesCache + pure coreDeps printPackageError :: PackageName -> String printPackageError p = " - " <> PackageName.print p <> "\n" @@ -651,7 +683,7 @@ getTransitiveDepsFromPackageSet packageSet deps = do -- recur here, as we need to get the transitive tree, not just the first level void $ forWithIndex dependenciesMap - (\dependency _ -> go (Set.insert dep seen) dependency) + \dependency _ -> go (Set.insert dep seen) dependency -- Errors may have changed after running through the child deps errorsAfterTransitiveDeps <- State.gets _.errors @@ -697,3 +729,13 @@ cycleError :: PackageName -> TransitiveDepsResult -> TransitiveDepsResult cycleError dep result = result { errors { cycle = Set.insert dep result.errors.cycle } } +-- | When we work with dependencies, we need to keep track of them separately +-- | for core and test, because they need to be treated differently in some +-- | contexts. +type ByEnv a = { core :: a, test :: a } + +onEachEnv :: ∀ a b. (a -> b) -> ByEnv a -> ByEnv b +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 diff --git a/src/Spago/Command/Publish.purs b/src/Spago/Command/Publish.purs index 1b30878ae..5d9d87f50 100644 --- a/src/Spago/Command/Publish.purs +++ b/src/Spago/Command/Publish.purs @@ -15,6 +15,7 @@ import Data.Formatter.DateTime as DateTime import Data.List as List import Data.Map as Map import Data.String as String +import Dodo (break, lines) import Effect.Aff (Milliseconds(..)) import Effect.Aff as Aff import Effect.Ref as Ref @@ -74,7 +75,7 @@ type PublishEnv a = type PublishArgs = {} -publish :: forall a. PublishArgs -> Spago (PublishEnv a) Operation.PublishData +publish :: ∀ a. PublishArgs -> Spago (PublishEnv a) Operation.PublishData publish _args = do -- We'll store all the errors here in this ref, then complain at the end resultRef <- liftEffect $ Ref.new (Left List.Nil) @@ -116,8 +117,8 @@ publish _args = do Effect.liftEffect Process.exit -- We then need to check that the dependency graph is accurate. If not, queue the errors - let allDependencies = Fetch.toAllDependencies dependencies - let globs = Build.getBuildGlobs { selected: NEA.singleton selected, withTests: false, dependencies: allDependencies, depsOnly: false } + let allCoreDependencies = Fetch.toAllDependencies $ dependencies <#> _ { test = Map.empty } + let globs = Build.getBuildGlobs { selected: NEA.singleton selected, withTests: false, dependencies: allCoreDependencies, depsOnly: false } eitherGraph <- Graph.runGraph globs [] case eitherGraph of Right graph -> do @@ -206,15 +207,16 @@ publish _args = do RegistryVersion v -> Right (Tuple pkgName v) _ -> Left pkgName ) - $ (Map.toUnfoldable allDependencies :: Array _) - if Array.length fail > 0 then do + $ (Map.toUnfoldable allCoreDependencies :: Array _) + if Array.length fail > 0 then addError $ toDoc [ "Some of the packages you specified as `extraPackages` do not point to the Registry." , "To be able to publish a package to the registry, all of its dependencies have to be packages registered on the Registry." , "Please replace the following packages with versions that are present in the Registry:" -- TODO point to docs ] - <> toDoc (map (indent <<< toDoc <<< (append "- ") <<< Json.stringifyJson PackageName.codec) fail) + <> break + <> lines (fail <#> \p -> indent $ toDoc $ "- " <> PackageName.print p) else do -- All dependencies come from the registry so we can trust the build plan. -- We can then try to build with the dependencies from there. @@ -352,7 +354,7 @@ publish _args = do -- from the solver (this is because the build might terminate the process, and we shall output the errors first) logInfo "Building again with the build plan from the solver..." let buildPlanDependencies = map Config.RegistryVersion resolutions - builtAgain <- runBuild { selected, dependencies: Map.singleton selected.package.name buildPlanDependencies } + builtAgain <- runBuild { selected, dependencies: Map.singleton selected.package.name { core: buildPlanDependencies, test: Map.empty } } ( Build.run { depsOnly: false , pursArgs: [] @@ -385,10 +387,10 @@ publish _args = do runSpago { purs: env.purs , git: env.git - , dependencies: dependencies + , dependencies , logOptions: env.logOptions , workspace: env.workspace { selected = Just selected } - , strictWarnings: (Nothing :: Maybe Boolean) + , strictWarnings: Nothing , pedanticPackages: false } action diff --git a/src/Spago/Config.purs b/src/Spago/Config.purs index a30058206..f38471148 100644 --- a/src/Spago/Config.purs +++ b/src/Spago/Config.purs @@ -449,15 +449,14 @@ rootPackageToWorkspacePackage { rootPackage, workspaceDoc } = do workspacePackageToLockfilePackage :: WorkspacePackage -> Tuple PackageName Lock.WorkspaceLockPackage workspacePackageToLockfilePackage { path, package } = Tuple package.name { path - , dependencies: package.dependencies - , test_dependencies: foldMap _.dependencies package.test - , build_plan: mempty -- Note: this is filled in later + , core: { dependencies: package.dependencies, build_plan: mempty } + , test: { dependencies: foldMap _.dependencies package.test, build_plan: mempty } } shouldComputeNewLockfile :: { workspace :: Core.WorkspaceConfig, workspacePackages :: Map PackageName WorkspacePackage } -> Lock.WorkspaceLock -> Boolean shouldComputeNewLockfile { workspace, workspacePackages } workspaceLock = -- the workspace packages should exactly match, except for the needed_by field, which is filled in during build plan construction - (map (workspacePackageToLockfilePackage >>> snd) workspacePackages /= (map (_ { build_plan = mempty }) workspaceLock.packages)) + ((workspacePackageToLockfilePackage >>> snd <$> workspacePackages) /= (eraseBuildPlan <$> workspaceLock.packages)) -- and the extra packages should exactly match || (fromMaybe Map.empty workspace.extraPackages /= workspaceLock.extra_packages) -- and the package set address needs to match - we have no way to match the package set contents at this point, so we let it be @@ -468,6 +467,8 @@ shouldComputeNewLockfile { workspace, workspacePackages } workspaceLock = Just (Core.SetFromPath _) -> true _ -> false ) + where + eraseBuildPlan = _ { core { build_plan = mempty }, test { build_plan = mempty } } getPackageLocation :: PackageName -> Package -> FilePath getPackageLocation name = Paths.mkRelative <<< case _ of diff --git a/src/Spago/Lock.purs b/src/Spago/Lock.purs index 7049c31a6..4cb06a195 100644 --- a/src/Spago/Lock.purs +++ b/src/Spago/Lock.purs @@ -1,14 +1,15 @@ module Spago.Lock - ( Lockfile - , lockfileCodec + ( GitLock , LockEntry(..) - , lockEntryCodec - , PathLock - , GitLock + , Lockfile , PackageSetInfo + , PathLock , RegistryLock , WorkspaceLock , WorkspaceLockPackage + , WorkspaceLockPackageEnv + , lockEntryCodec + , lockfileCodec ) where import Spago.Prelude @@ -53,9 +54,13 @@ type PackageSetInfo = } type WorkspaceLockPackage = - { dependencies :: Core.Dependencies - , test_dependencies :: Core.Dependencies + { core :: WorkspaceLockPackageEnv + , test :: WorkspaceLockPackageEnv , path :: FilePath + } + +type WorkspaceLockPackageEnv = + { dependencies :: Core.Dependencies , build_plan :: Set PackageName } @@ -74,8 +79,12 @@ workspaceLockCodec = CJ.named "WorkspaceLock" $ CJ.object where dependenciesCodec = CJ.named "Dependencies" $ CJ.object $ CJ.recordProp (Proxy @"path") CJ.string + $ CJ.recordProp (Proxy @"core") envCodec + $ CJ.recordProp (Proxy @"test") envCodec + $ CJ.record + + envCodec = CJ.named "Environment" $ CJ.object $ CJ.recordProp (Proxy @"dependencies") Config.dependenciesCodec - $ CJ.recordProp (Proxy @"test_dependencies") Config.dependenciesCodec $ CJ.recordProp (Proxy @"build_plan") (CJ.Common.set PackageName.codec) $ CJ.record diff --git a/test-fixtures/build/local-package-set-lockfile/spago.lock.new b/test-fixtures/build/local-package-set-lockfile/spago.lock.new index 3fce14642..6eb97c4e1 100644 --- a/test-fixtures/build/local-package-set-lockfile/spago.lock.new +++ b/test-fixtures/build/local-package-set-lockfile/spago.lock.new @@ -2,15 +2,18 @@ workspace: packages: local-package-set-lockfile: path: ./ - dependencies: - - console - - effect - - prelude - test_dependencies: [] - build_plan: - - console - - effect - - prelude + core: + dependencies: + - console + - effect + - prelude + build_plan: + - console + - effect + - prelude + test: + dependencies: [] + build_plan: [] package_set: address: path: ./local-package-set.json diff --git a/test-fixtures/build/local-package-set-lockfile/spago.lock.old b/test-fixtures/build/local-package-set-lockfile/spago.lock.old index 7d6878645..7285f3e47 100644 --- a/test-fixtures/build/local-package-set-lockfile/spago.lock.old +++ b/test-fixtures/build/local-package-set-lockfile/spago.lock.old @@ -2,15 +2,18 @@ workspace: packages: local-package-set-lockfile: path: ./ - dependencies: - - console - - effect - - prelude - test_dependencies: [] - build_plan: - - console - - effect - - prelude + core: + dependencies: + - console + - effect + - prelude + build_plan: + - console + - effect + - prelude + test: + dependencies: [] + build_plan: [] package_set: address: path: ./local-package-set.json diff --git a/test-fixtures/monorepo/case3-shared-dependencies2/package-b/spago.yaml b/test-fixtures/monorepo/case3-shared-dependencies2/package-b/spago.yaml index db3bfa76f..10e425292 100644 --- a/test-fixtures/monorepo/case3-shared-dependencies2/package-b/spago.yaml +++ b/test-fixtures/monorepo/case3-shared-dependencies2/package-b/spago.yaml @@ -7,5 +7,3 @@ package: main: Test.PACKAGE.B dependencies: - console - - effect - - prelude diff --git a/test-fixtures/monorepo/case3-shared-dependencies2/package-c/spago.yaml b/test-fixtures/monorepo/case3-shared-dependencies2/package-c/spago.yaml index 20eeeb986..29f174873 100644 --- a/test-fixtures/monorepo/case3-shared-dependencies2/package-c/spago.yaml +++ b/test-fixtures/monorepo/case3-shared-dependencies2/package-c/spago.yaml @@ -7,4 +7,3 @@ package: dependencies: - console - effect - - prelude diff --git a/test-fixtures/monorepo/case3-shared-dependencies2/package-c/test/Main.purs b/test-fixtures/monorepo/case3-shared-dependencies2/package-c/test/Main.purs new file mode 100644 index 000000000..5a9d99ffa --- /dev/null +++ b/test-fixtures/monorepo/case3-shared-dependencies2/package-c/test/Main.purs @@ -0,0 +1,11 @@ +module Test.PACKAGE.C where + +import Prelude + +import Effect (Effect) +import Effect.Console (log) +import Src.PACKAGE.C as PACKAGE.C + +main :: Effect Unit +main = do + log $ "Test for " <> PACKAGE.C.packageNameValue diff --git a/test-fixtures/polyrepo.lock b/test-fixtures/polyrepo.lock index 2fcd7c3ef..b3ea2c4e1 100644 --- a/test-fixtures/polyrepo.lock +++ b/test-fixtures/polyrepo.lock @@ -2,44 +2,55 @@ workspace: packages: package-a: path: package-a - dependencies: - - package-b - - package-c - - prelude - test_dependencies: - - console - - effect - - prelude - build_plan: - - console - - effect - - package-b - - package-c - - prelude + core: + dependencies: + - package-b + - package-c + - prelude + build_plan: + - package-b + - package-c + - prelude + test: + dependencies: + - console + - effect + - prelude + build_plan: + - console + - effect + - prelude package-b: path: package-b - dependencies: - - package-c - - prelude - test_dependencies: - - console - - effect - - prelude - build_plan: - - console - - effect - - package-c - - prelude + core: + dependencies: + - package-c + - prelude + build_plan: + - package-c + - prelude + test: + dependencies: + - console + build_plan: + - console + - effect + - prelude package-c: path: package-c - dependencies: - - prelude - test_dependencies: - - console - - effect - - prelude - build_plan: - - prelude + core: + dependencies: + - prelude + build_plan: + - prelude + test: + dependencies: + - console + - effect + build_plan: + - console + - effect + - prelude package_set: address: registry: 0.0.1 diff --git a/test-fixtures/publish-extra-package-core-dependency.txt b/test-fixtures/publish-extra-package-core-dependency.txt new file mode 100644 index 000000000..8b8f632bb --- /dev/null +++ b/test-fixtures/publish-extra-package-core-dependency.txt @@ -0,0 +1,20 @@ +Reading Spago workspace configuration... + +✓ Selecting package to build: aaa + +Downloading dependencies... +Building... + Src Lib All +Warnings 0 0 0 +Errors 0 0 0 + +✓ Build succeeded. + +Your package "aaa" is not ready for publishing yet, encountered 1 error: + + +✘ Some of the packages you specified as `extraPackages` do not point to the Registry. +To be able to publish a package to the registry, all of its dependencies have to be packages registered on the Registry. +Please replace the following packages with versions that are present in the Registry: + - effect + - maybe diff --git a/test-fixtures/publish/extra-package-core/spago.yaml b/test-fixtures/publish/extra-package-core/spago.yaml new file mode 100644 index 000000000..916de40e2 --- /dev/null +++ b/test-fixtures/publish/extra-package-core/spago.yaml @@ -0,0 +1,30 @@ +package: + name: aaa + dependencies: + - console: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.1 <7.0.0" + - maybe: ">=6.0.0 <7.0.0" + test: + main: Test.Main + dependencies: + - effect + publish: + version: 0.0.1 + license: MIT + location: + githubOwner: purescript + githubRepo: aaa +workspace: + packageSet: + registry: 28.1.1 + extraPackages: + console: "6.1.0" + maybe: + repo: https://github.com/purescript/purescript-maybe.git + version: v6.0.0 + dependencies: ["prelude", "control", "invariant", "newtype"] + effect: + repo: https://github.com/purescript/purescript-effect.git + version: v4.0.0 + dependencies: ["prelude"] diff --git a/test-fixtures/publish/extra-package-core/src/Main.purs b/test-fixtures/publish/extra-package-core/src/Main.purs new file mode 100644 index 000000000..549e1681a --- /dev/null +++ b/test-fixtures/publish/extra-package-core/src/Main.purs @@ -0,0 +1,10 @@ +module Lib where + +import Prelude + +import Data.Maybe (Maybe(..), isNothing) +import Effect (Effect) +import Effect.Console (logShow) + +printNothing :: Effect Unit +printNothing = logShow $ isNothing Nothing diff --git a/test-fixtures/publish/extra-package-core/test/Main.purs b/test-fixtures/publish/extra-package-core/test/Main.purs new file mode 100644 index 000000000..f45a41277 --- /dev/null +++ b/test-fixtures/publish/extra-package-core/test/Main.purs @@ -0,0 +1,8 @@ +module Test.Main where + +import Prelude + +import Effect (Effect) + +main :: Effect Unit +main = pure unit diff --git a/test-fixtures/publish/extra-package-test/spago.yaml b/test-fixtures/publish/extra-package-test/spago.yaml new file mode 100644 index 000000000..0d56c752b --- /dev/null +++ b/test-fixtures/publish/extra-package-test/spago.yaml @@ -0,0 +1,25 @@ +package: + name: aaa + dependencies: + - console: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.1 <7.0.0" + test: + main: Test.Main + dependencies: + - maybe + publish: + version: 0.0.1 + license: MIT + location: + githubOwner: purescript + githubRepo: aaa +workspace: + packageSet: + registry: 28.1.1 + extraPackages: + console: "6.1.0" + maybe: + repo: https://github.com/purescript/purescript-maybe.git + version: v6.0.0 + dependencies: ["prelude", "control", "invariant", "newtype"] diff --git a/test-fixtures/publish/extra-package-test/src/Main.purs b/test-fixtures/publish/extra-package-test/src/Main.purs new file mode 100644 index 000000000..1a4bfa596 --- /dev/null +++ b/test-fixtures/publish/extra-package-test/src/Main.purs @@ -0,0 +1,9 @@ +module Lib where + +import Prelude + +import Effect (Effect) +import Effect.Console (log) + +printFoo :: Effect Unit +printFoo = log "foo" diff --git a/test-fixtures/publish/extra-package-test/test/Main.purs b/test-fixtures/publish/extra-package-test/test/Main.purs new file mode 100644 index 000000000..1be9b1627 --- /dev/null +++ b/test-fixtures/publish/extra-package-test/test/Main.purs @@ -0,0 +1,11 @@ +module Test.Main where + +import Prelude + +import Data.Maybe (Maybe(..), isNothing) +import Effect (Effect) +import Effect.Console (logShow) + +main :: Effect Unit +main = + logShow $ isNothing Nothing diff --git a/test-fixtures/spago-with-maybe.lock b/test-fixtures/spago-with-maybe.lock index fb7085d92..a03f33654 100644 --- a/test-fixtures/spago-with-maybe.lock +++ b/test-fixtures/spago-with-maybe.lock @@ -2,22 +2,25 @@ workspace: packages: aaa: path: ./ - dependencies: - - console - - effect - - maybe - - prelude - test_dependencies: [] - build_plan: - - console - - control - - effect - - invariant - - maybe - - newtype - - prelude - - safe-coerce - - unsafe-coerce + core: + dependencies: + - console + - effect + - maybe + - prelude + build_plan: + - console + - control + - effect + - invariant + - maybe + - newtype + - prelude + - safe-coerce + - unsafe-coerce + test: + dependencies: [] + build_plan: [] package_set: address: registry: 33.0.0 diff --git a/test-fixtures/spago.lock b/test-fixtures/spago.lock index ec80c9f3d..dfb8069e1 100644 --- a/test-fixtures/spago.lock +++ b/test-fixtures/spago.lock @@ -2,15 +2,18 @@ workspace: packages: aaa: path: ./ - dependencies: - - console - - effect - - prelude - test_dependencies: [] - build_plan: - - console - - effect - - prelude + core: + dependencies: + - console + - effect + - prelude + build_plan: + - console + - effect + - prelude + test: + dependencies: [] + build_plan: [] package_set: address: registry: 33.0.0 diff --git a/test/Spago/Lock.purs b/test/Spago/Lock.purs index c9d3f0abb..cad7f9f82 100644 --- a/test/Spago/Lock.purs +++ b/test/Spago/Lock.purs @@ -33,23 +33,33 @@ validLockfile = { workspace: { packages: Map.fromFoldable [ packageTuple "my-app" - { dependencies: Dependencies $ Map.fromFoldable - [ packageTuple "effect" (Just (unsafeFromRight (Range.parse ">=1.0.0 <5.0.0"))) - , packageTuple "my-library" Nothing - ] - , test_dependencies: Dependencies Map.empty + { core: + { dependencies: Dependencies $ Map.fromFoldable + [ packageTuple "effect" (Just (unsafeFromRight (Range.parse ">=1.0.0 <5.0.0"))) + , packageTuple "my-library" Nothing + ] + , build_plan: Set.fromFoldable + [ mkPackageName "my-library" + , mkPackageName "effect" + , mkPackageName "prelude" + ] + } + , test: + { dependencies: Dependencies Map.empty + , build_plan: Set.empty + } , path: "my-app" - , build_plan: Set.fromFoldable - [ mkPackageName "my-library" - , mkPackageName "effect" - , mkPackageName "prelude" - ] } , packageTuple "my-library" - { dependencies: Dependencies $ Map.fromFoldable [ packageTuple "prelude" Nothing ] - , test_dependencies: Dependencies $ Map.fromFoldable [ packageTuple "console" (Just Config.widestRange) ] + { core: + { dependencies: Dependencies $ Map.fromFoldable [ packageTuple "prelude" Nothing ] + , build_plan: Set.fromFoldable [ mkPackageName "prelude" ] + } + , test: + { dependencies: Dependencies $ Map.fromFoldable [ packageTuple "console" (Just Config.widestRange) ] + , build_plan: Set.fromFoldable [ mkPackageName "console" ] + } , path: "my-library" - , build_plan: Set.fromFoldable [ mkPackageName "prelude" ] } ] , package_set: Just @@ -115,23 +125,30 @@ workspace: packages: my-app: path: my-app - build_plan: - - effect - - my-library - - prelude - dependencies: - - effect: ">=1.0.0 <5.0.0" - - my-library - test_dependencies: [] + core: + build_plan: + - effect + - my-library + - prelude + dependencies: + - effect: ">=1.0.0 <5.0.0" + - my-library + test: + dependencies: [] + build_plan: [] my-library: path: my-library - build_plan: - - prelude - dependencies: - - prelude - test_dependencies: - - console: "*" + core: + build_plan: + - prelude + dependencies: + - prelude + test: + dependencies: + - console: "*" + build_plan: + - console package_set: address: diff --git a/test/Spago/Publish.purs b/test/Spago/Publish.purs index 2f2f6662a..16c8626de 100644 --- a/test/Spago/Publish.purs +++ b/test/Spago/Publish.purs @@ -50,6 +50,13 @@ spec = Spec.around withTempDir do git [ "remote", "set-url", "origin", "git@github.com:purescript/bbb.git" ] >>= shouldBeSuccess spago [ "publish", "--offline" ] >>= shouldBeFailureErr (fixture "publish-invalid-location.txt") + Spec.it "fails if a core dependency is not in the registry" \{ spago, fixture } -> do + FS.copyTree { src: fixture "publish/extra-package-core", dst: "." } + spago [ "build" ] >>= shouldBeSuccess + doTheGitThing + spago [ "fetch" ] >>= shouldBeSuccess + spago [ "publish", "--offline" ] >>= shouldBeFailureErr (fixture "publish-extra-package-core-dependency.txt") + Spec.it "can get a package ready to publish" \{ spago, fixture } -> do FS.copyFile { src: fixture "spago-publish.yaml", dst: "spago.yaml" } FS.mkdirp "src" @@ -60,6 +67,13 @@ spec = Spec.around withTempDir do spago [ "fetch" ] >>= shouldBeSuccess spago [ "publish", "--offline" ] >>= shouldBeFailureErr (fixture "publish.txt") + Spec.it "allows to publish with a test dependency not in the registry" \{ spago, fixture } -> do + FS.copyTree { src: fixture "publish/extra-package-test", dst: "." } + spago [ "build" ] >>= shouldBeSuccess + doTheGitThing + spago [ "fetch" ] >>= shouldBeSuccess + spago [ "publish", "--offline" ] >>= shouldBeFailureErr (fixture "publish.txt") + doTheGitThing :: Aff Unit doTheGitThing = do git [ "init" ] >>= shouldBeSuccess