invertase / melos

πŸŒ‹ A tool for managing Dart projects with multiple packages. With IntelliJ and Vscode IDE support. Supports automated versioning, changelogs & publishing via Conventional Commits.
https://melos.invertase.dev/~melos-latest
Apache License 2.0
1.12k stars 199 forks source link

fix: Melos interferes with project dependencies #567

Closed wujek-srujek closed 6 months ago

wujek-srujek commented 1 year ago

Is there an existing issue for this?

Version

3.1.1

Description

I have a project with a root package (the main app) and some packages in the packages folder, the main app uses paths to the packages. When I invoke flutter pub get in all of the packages, and then in the main package, git diff shows me nothing.

Now I add Melos, the only changes are:

  1. I add melos: ^3.1.1 to dev_dependencies.
  2. I add melos.yaml.

Here is the diff (git diff --staged .):

diff --git a/melos.yaml b/melos.yaml
new file mode 100644
index 0000000..4ff8fe6
--- /dev/null
+++ b/melos.yaml
@@ -0,0 +1,5 @@
+name: myproject
+
+packages:
+  - .
+  - packages/*
diff --git a/pubspec.yaml b/pubspec.yaml
index 236f056..f63036f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -32,6 +32,7 @@ dev_dependencies:
   flutter_test:
     sdk: flutter
   hive_generator: ^2.0.0
+  melos: ^3.1.1
   mocktail: ^1.0.0

 flutter:

Calling melos bootstrap adds quite some stuff to pubpec.lock, but surprisingly it also downgrades one of the transitive dependencies:

@@ -237,10 +277,10 @@ packages:
     dependency: transitive
     description:
       name: file
-      sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
+      sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
       url: "https://pub.dev"
     source: hosted
-    version: "7.0.0"
+    version: "6.1.4"
   fixnum:
     dependency: transitive
     description:

Without Melos, the file dependency is coming through build_runner -> glob (^2.0.0: https://github.com/dart-lang/build/blob/master/build_runner/pubspec.yaml#L28) -> file (file: '>=6.1.3 <8.0.0': https://github.com/dart-lang/glob/blob/master/pubspec.yaml#L12) and is set to 7.0.0 as of right now; introducing Melos downgrades it because of this: https://github.com/invertase/melos/blob/main/packages/melos/pubspec.yaml#L24

In this case it will all work, and it is also easy to fix for you - you can very likely update the version specification to also allow ^7.0.0, just like glob does; but in the general case, is it a good idea for the melos dependency, which is basically 'just' a binary, to force package downgrades?

Now imagine (or follow the 'steps to reproduce' section) that I actually have a dependency on file: ^7.0.0 in my pubspec.yaml - the project will fail to build because of Melos interfering.

Steps to reproduce

  1. Create a new Flutter project and replace pubspec.yaml with this:

    name: foobarbaz
    
    environment:
     sdk: ^3.0.6
    
    dependencies:
     flutter:
       sdk: flutter
    
    dev_dependencies:
     build_runner: ^2.4.6
  2. Invoke flutter pub get - the file dependency is set to 7.0.0 as of right now.
  3. Add Melos dependency and invoke melos bootstrap - the file dependnecy is downgraded to 6.1.4 as of now.
  4. Add an direct dependency on file: ^7.0.0 to pubspec.yaml.
  5. Invoke melos bootstrap - this fails due to flutter pub get failure with the following cause:
Resolving dependencies...
Because no versions of melos match >3.1.1 <4.0.0 and melos 3.1.1 depends on file ^6.1.0, melos ^3.1.1 requires file ^6.1.0.
So, because foobarbaz depends on both file ^7.0.0 and melos ^3.1.1, version solving failed.

Expected behavior

Melos, which is an external tool, doesn't influence the dependencies in my project. For example, I don't expect, say, fvm to be in my pubspec and force versions upon me.

Screenshots

No response

Additional context and comments

I do understand why you want to put it into pubspec.yaml (make sure developers use the same version consistently), but the cost is great as it messes around with project dependencies, and there are solutions for this to not be the case.

I can imagine a different strategy for Melos:

  1. A Melos-enabled project has a file .melos.version (or similar) which contains the version to use in the project.
  2. The globally activated melos binary is a shim that can check this file and make sure there is a locally installed version that corresponds to it: if it exists in its 'cache' it uses it, if not, it downloads it and uses it.
  3. After a change to this .melos.version file, when the melos binary is called, it will notice a new version and look for it locally and download and cache if needed.

This would allow project developers to share the version of Melos, but do so in a way that doesn't interfere with the dependencies of their project. This strategy is also nothing new, this is how tools like Gradle Wrapper (https://docs.gradle.org/current/userguide/gradle_wrapper.html), Maven Wrapper (https://maven.apache.org/wrapper/) and others work, FVM does something similar with its .fvm/flutter_sdk file.

I guess this bug could also be considered a feature/improvement request.

spydon commented 8 months ago

You shouldn't have a package/app in the root of the monorepo, that's where the workspace pubspec lives and it should not be mixed with the dependencies of your main app/package.

I recommend that you use a directory structure like this:

mono_repo
β”œβ”€β”€ apps
β”‚Β Β  └── my_app
β”‚Β Β      └── pubspec.yaml
β”œβ”€β”€ packages
β”‚Β Β  └── my_package
β”‚Β Β      └── pubspec.yaml
β”œβ”€β”€ melos.yaml
└── pubspec.yaml
wujek-srujek commented 8 months ago

It would be very helpful if this were mentioned in the Melos documentation somewhere, like a 'recommended repository structure' section. AFAIK as I was starting off with 3.1.1 the documentation didn't mention this. To the contrary, this does imply that having a Melos-managed project in the root of the monorepo is a normal thing to do - it mentions specifying . in the packages list.

spydon commented 8 months ago

Yeah, I agree that this should be updated, and with a suggested directory structure.

spydon commented 6 months ago

It is now mentioned in the documentation.