Managing multi-package Flutter projects with Melos
Most of the time when you create a Flutter project. You work with a single package. The project consists of a single pubspec.yaml
, lib
folder. You put all your features and utilities in the same package. But there are projects which break their features and utilities into multiple packages. This helps in better separation of concerns and allows the team to open-source some of their packages. Here is a diagram of how a multi-package project will look like:
Here we have divided the project into three layers. The first layer is the root project which will hold common configurations that applies to all the different packages in the project. The 2nd layer is having independent feature packages that do not depend on each other. The 3rd layer consists of utility packages that are used in multiple feature packages. I won’t dive deep into how to create or structure a multi-package flutter project. This article will focus on solving a particular challenge that is typically faced in a multi-package flutter project.
The Challenge
In a single package flutter project running the following tasks is dead simple:
flutter pub get
flutter test
flutter analyze
- Generating code coverage i.e
flutter test --coverage
But running the same tasks in a multi-package flutter project can be challenging because you need to run those tasks in every package inside your project and give a summarised result at the end of completion of the task. Now we know what the challenge is. Let’s discuss what are the possible solutions for it.
The Solution
There are 2 possible solutions to solve this challenge. Let’s look at the first one:
- Writing bash scripts for various tasks.
This is definitely a solution but not a smart solution. You need to first write a script that will find out all the packages in your project and run one of the above tasks in it. You also need to make sure that you display output in a pretty format to make things readable. If you are more of a GUI guy then you need to create some sort of configuration in your IDE to run your scripts through GUI. - Integrating Melos into your project.
This is a smart solution over the first one which I highly recommend. So let’s discuss in details what Melos is and how to integrate it into your multi-package project.
Introducing Melos
Melos is a CLI tool for managing Flutter/Dart project with multiple packages.
Melos is developed by a well-known team in the Flutter community i.e invertase. You can read in detail about Melos on their website but here is a quick list of features Melos provide:
- Automatic versioning & changelog generation.
- Automated publishing of packages to
pub.dev
. - Local package linking and installation.
- Executing simultaneous commands across packages.
- Listing of local packages & their dependencies.
Now let’s see how we can perform all the above tasks using Melos.
Note: Please download the starter project if you wanna learn by doing. There is another branch where you can find the final version of the project.
Installing Melos
Let’s install Melos first. I assume you have already installed Flutter SDK and set the Flutter and Dart path to bash_profile
. Run the following command in your terminal:
The next step is to open up your starter project in your IDE. I prefer using Intellij and would show you some awesome GUI features provided by Melos. The project structure should look like the following:
Now create a file named melos.yaml
in your root app. and copy the following content to it:
Let’s understand the above script:
a) name
: You have to give the name of your project. You can find it inside the pubspec.yaml
of the root project.
b) packages
: This list should contain paths to the individual packages within your project. Each path can be defined using the glob pattern expansion format.
Melos Bootstrap
Now run the following command in your terminal from your root project to link all your local packages together and update the dependencies i.e flutter pub get
.
You should see the following output after running the above command:
You can read why you need to bootstrap Melos here. To be precise it’s one of the important commands you should execute when setting up Melos in a project or you performed a cleanup in your project.
Melos Clean
You can execute this command when you want to remove temporary files from your project(build artifacts, pub files etc). This is how the command looks like:
Commands
Now you will work on creating different commands to achieve the tasks which we mentioned at the starting of the article.
a. Running test cases in specific packages
Write the following command in your melos.yaml
file:
Let’s understand what’s happening in the above script:
1) You have created a custom script i.e test:selective_unit_test
that once executed will show you an option to select a package whose unit tests you wanna run.
2) Melos provide powerful filtering options to select packages that meet the filter criteria. In the above script, you used --dir-exists="test"
as the filtering option. This will filter all the packages which consist of test
folder. You can find more filtering options on their website.
3) --fail-fast
will immediately terminate the script execution if it encounters a failing test case.
4) You can give a human-readable description to each script by using the description
section.
5) You must be wondering why did you name this command like test:selective_unit_test
. The next command will answer your question. 😃
6) You can read in detail what melos exec
does. Basically, it will execute the command or script in every package inside your project.
Now run the following command:
You will be seeing the following output:
The above command was able to find out these packages which contained the folder test. Enter 2 as the option and you will see the following output:
b. Running test cases in all the packages
Now write the following command which will run all the unit test cases in the project. This won’t prompt any option selection:
Let’s discuss what the above command does:
- This command will basically run the previous command with
--no-select
as an argument. That means, run all the unit tests. - You can use
melos run test
to run this command. You named this commandtest
because there can be multiple variants of test commands like you created in the previous step i.etest:selective_unit_test
. You can also create more variants e.gtest:e2e_test
,test:bdd_test
etc. You can combine all the variants together and run in a single command i.etest
.
c. Running analyze
in all the packages:
Create the following command under the script section:
Nothing fancy here. You can execute melos run analyze
to run the analyze
in all the packages.
d. Generating code coverage of the whole project:
To generate the code coverage for the whole project. There is a custom script included in the project i.e combine_coverage.sh
. This will basically merge all the lcov.info
files from different packages into one lcov.info
file. Then you can use this method to convert lcov.info
file to HTML.
Write the following command under the script section:
MELOS_ROOT_PATH
will give you the path where melos.yaml
is stored i.e root project. After the script’s execution has been completed. You can see the coverage_report
folder in the project. Now you have one lcov.info
file which will give you a report of the whole project.
Finally, your melos.yaml
file will look something like this:
GUI options
If you don’t want to execute these commands through the terminal and want to run them using GUI then Melos got you covered. After adding all the commands you can run the bootstrap command again. This will generate some configuration and you can see some GUI options like this:
Now you can execute all these commands without typing anything in the terminal. 😄
Next Steps
This is just the tip of the iceberg. You can learn much more filtering options and commands from the Melos website. Hope you enjoyed the article. Please do connect with me on Twitter and LinkedIn. Stay safe and stay healthy. 😃