Skip to content

Proposal for resolution: external as support for mono-repositories #4022

@sigurdm

Description

@sigurdm

NOTE: This proposal has been partially succeeded by a proposal with a proper workspace file. See https://docs.google.com/document/d/1UEEAGdWIgVf0X7o8WPQCPmL4LSzaGCfJTM0pURoDLfE/edit?resourcekey=0-c5CMaOoc_pg3ZwJKMAM0og . We probably want to support resolution: external as well as resolution: workspace for cases such as the dart sdk where the package-config is not created by pub.

This issue suggests a simple mechanism in the pub client for better supporting a shared resolution between the packages of a mono-repo. Namely adding

resolution: external

to a pubspec.yaml to indicate that pub should not treat it as a root package.

The main parts of this proposal does not contain much support in the pub client for how to manage multiple packages together in a single project. We instead suggest pushing more advanced support to external tools such as package:mono_repo or package:melos.

Motivation

Mono-repositories are repositories with tightly related packages that are often developed and released together.

The dart team is maintaining a number of mono-repos Eg:
dart-lang/test
google/protobuf.dart
dart-lang/ecosystem

Another special case of mono-repos is the dart SDK where a common resolution is used, but not maintained by pub, even though many of the subpackages have a pubspec.yaml, and some are published to pub.dev individually...

Externally we know of several customers using mono-repositories.

When developing in a mono-repo it is often desirable to use a shared version resolution for ensuring consistent behavior.

By having a shared resolution, tools such as the analyzer can also share context between analyzing each package, with big potential memory savings.

Some packages are in a mono-repo for organizational purposes, and might not want to share the same resolution (and would not benefit from this proposal). One such repo could be: flutter/packages.

Some tutorials are split in multiple "chapters" each with a single pubspec.yaml, and the analyzer has been known to choke on those because they each have a separate context. They could perhaps benefit from this proposal (if developing the pubspec.yaml is not part of the tutorial).

Existing support for mono-repositories

There exists tooling to support handling of mono-repos, such as mono_repo.dart and melos. These tools can help running the same command for all sub-projects
They, however, don't allow a shared resolution between the packages. These could be updated to allow for generating a shared root pubspec.yaml.

Proposed mechanism

A new section in pubspec.yaml would mark a pubspec that it should not be used for a "local resolution", but instead rely on the Dart VM's mechanism for searching parent directories for a .dart_tool/package_config.json containing a resolution to use.

name: test
environment:
  sdk: ^3.1.0
dependencies:
  ...
resolution: external
  • Given this, dart pub get would not generate a dart_tool/package_config.json file in the same directory. Instead it could either (still open):

    • Report an error: "This package is not intended for direct resolution"
    • Look for a pubspec.yaml in parent directories and resolve that instead.
    • Do nothing.
  • dart run and dart test needs to ignore pubspec.yamls with resolution: external when resolving packages.
    dart pub publish should however consider this pubspec.yaml when doing validations.
    We could create a lint discouraging from having dev_dependencies and dependency_overrides in a pubspec.yaml with resolution: external.

Creating a mono-repo

Now to create a mono-repo, one would create a "root" pubspec.yaml with dependency_overrides with path-dependencies on all the sub-projects.

name: my_mono_repo_root
publish_to: none
environment:
  sdk: ^3.0.0
dev_dependencies:
  build_runner: ^2.0.0
dependency_overrides:
  pkg1:
    path: pkgs/pkg1
  pkg2:
    path: pkgs/pkg2

All dev_dependencies should be collected in the root pubspec.yaml, and thus shared between the subprojects.

Running dart pub get in this root-directory will create a shared pubspec.lock (that can be checked into source control if desired) and a shared dart_tool/package_config.json for consumption by the Dart VM and analyzer.

  • We now rely on the dart run resolution mechanism to look up through parent directories for finding the dart_tool/package_config.json file, and providing the package resolution. This should enable dart test to still work from the sub-project folder (we need to test that this works).

  • One could think of mechanisms for "linking" the sub-projects into the root-project, such that they could be operated on as a whole (eg. when doing upgrades). We propose to (at least as a first step) leave such functionality out of the pub client, and for example

The Dart SDK

In the Dart SDK we can mark all the in-sdk-repo packages as resolution: external.

There will not (at least for now) be a root pubspec.yaml, but we will instead rely on the current alternative tooling to generate a shared dart_tool/package_config.json.

Open questions:

  • Do we need a way to mark the sub-dependencies as special, such that Pub knows how to update them in sync? Or are dependency_overrides good enough?
    • We could add this later if there's a high demand
    • We could also autodetect overridden path-dependencies from the root-package and handle them special.
  • We need to figure out how build_runner would work in such a setup. Can each sub-project contain its own build.yaml?
  • How does this affect dart pub add? Can you add to a sub-package, and should that resolve together with other packages?
  • Should dart pub add --dev find the root pubspec.yaml?
  • How does this affect dart pub upgrade --major-versions? Is there a way to upgrade all the pubspec.yaml's of a project in sync? (Could perhaps be added later)

We might need a way to force resolution of the single package, e.g. for deployment of a sub-package. (Could we piggy back on dart pub get --enforce-lockfile - then we should have called it dart pub get --deploy 😐)

Potential downsides

  • Relying on dependencies_overrides can cause some issues
    • Dependency constraints among sub-projects are not checked.
      When publishing you might have constraints not matching sibling dependencies

      This can to some extent be helped by doing a sub-package-only resolution in dart pub publish.

  • Diamonds where you depend on other packages on pub.dev, and these packages also depends on another package in your mono-repository
    • These are rather rare - perhaps we don't need strong support for those.
  • cycles with other packages on pub.dev
    • These are rather rare - perhaps we don't need to support those.
  • Mono-repository root pubspec.yaml might be missing the dependency_override that should include a child.
    • Could we warn against this?

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions