Monorepo with Melos

This page covers the basics of how to setup and use Melos, a Dart/Flutter focused monorepo tool, so you can use it to manage your own monorepo.

While you can, in theory, go about this guide without using Melos, using Melos will save you a lot of time and headache. There is some slight upfront cost of configuring and learning how Melos works, but it pays off substantially in the long-term.

The rest of this guide assumes you are using Melos, so here are the steps to setup Melos, along with some common commands.

It is also highly recommended that you read the Melos documentation linked above, as this page only covers the bare minimum and it is likely you will want to do more than listed here.

/melos.yaml

Here is a sample of Melos' configuration file to get you started:

name: library_name

repository: https://github.com/YourGitHubAccount/library_name

packages:
  - packages/**

scripts:
  analyze:
    exec: flutter analyze .
    description: Analyze a specific package in this project.

  check-format:
    exec: dart format --set-exit-if-changed .
    description: Check the format of a specific package in this project.

  format:
    exec: dart format .
    description: Format a specific package in this project.

  version:
    description: Updates version numbers in all build files
    run: bash scripts/version.sh

  build:
    run: melos run build:apple && melos run build:android && melos run build:other
    description: Build all native libraries for the project.

  build:apple:
    run: bash scripts/build-apple.sh
    description: Build the XCFramework for iOS and macOS.

  build:android:
    run: bash scripts/build-android.sh
    description: Build the .tar.gz for Android.

  build:other:
    run: bash scripts/build-other.sh
    description: Build the .tar.gz for all other platforms.

  test:
    run: melos run test:dart --no-select && melos run test:flutter --no-select
    description: Run all Dart & Flutter tests in this project.

  test:dart:
    run: melos exec -c 1 --fail-fast -- "dart test test"
    description: Run Dart tests for a specific package in this project.
    select-package:
      flutter: false
      dir-exists: test

  test:flutter:
    run: melos exec -c 1 --fail-fast -- "flutter test test"
    description: Run Flutter tests for a specific package in this project.
    select-package:
      flutter: true
      dir-exists: test

You can run the melos "scripts" defined in this file with melos run ..., e.g. melos run build:android to build a .tar.gz for Android devices. Also, when you first setup your Melos repo, you will need to run melos bootstrap (or melos bs for short). To clean your repo in the future, you can run melos clean && melos bs.

/scripts/version.sh

Every time you need to make a new release of your library, Melos will take care of the heavy lifting for you. Melos creates new versions via the simple command, melos version. melos version creates and manages git tags, in addition to automatically incrementing the version numbers appropriately.

Since we are distributing our binaries separately from the Dart/Flutter packages on pub.dev, we take advantage of a special "melos script" defined in the configuration file, named "version". In this versioning script, we change the version numbers for our Flutter build process so that consumers of our library will always get the binaries associated with their version.

Replace all instances of library_name below with your library name.

#!/bin/bash

CURR_VERSION=library_name-v`awk '/^version: /{print $2}' packages/library_name/pubspec.yaml`

# iOS & macOS
APPLE_HEADER="release_tag_name = '$CURR_VERSION' # generated; do not edit"
sed -i.bak "1 s/.*/$APPLE_HEADER/" packages/flutter_library_name/ios/flutter_library_name.podspec
sed -i.bak "1 s/.*/$APPLE_HEADER/" packages/flutter_library_name/macos/flutter_library_name.podspec
rm packages/flutter_library_name/macos/*.bak packages/flutter_library_name/ios/*.bak

# CMake platforms (Linux, Windows, and Android)
CMAKE_HEADER="set(LibraryVersion \"$CURR_VERSION\") # generated; do not edit"
for CMAKE_PLATFORM in android linux windows
do
    sed -i.bak "1 s/.*/$CMAKE_HEADER/" packages/flutter_library_name/$CMAKE_PLATFORM/CMakeLists.txt
    rm packages/flutter_library_name/$CMAKE_PLATFORM/*.bak
done

git add packages/flutter_library_name/

Conventional Commits

For Melos versioning to work, which our monorepo relies on to distribute binaries properly, you need to use "conventional commits." If you are not familiar with conventional commits, that is ok. Simply read up on conventional commits in the Melos guide.