A monorepo is a single repository that stores the source code of several or all packages of an organization. One of the biggest advantages of using monorepos is that it's easier to share and reuse code across multiple packages inside the monorepo. However, when you want to publish one of the packages for others to use, or if you need to use one of the packages from a monorepo in other projects, things get a bit more complicated. In this post, we will look into creating standalone Composer packages from a monorepo with Private Packagist multipackages and installing them with Composer.

An example project structure for the ACME CMS. There are three plugins (cache, debug, and logger) which each have their own composer.json file in a subdirectory.

Reasons to keep multiple packages in a single VCS repository

The advantages of storing multiple Composer packages in a single VCS repository (Git, Mercurial, Subversion, etc) include:

  • Ability to make changes across all packages in a single commit/PR (rather than having to create a separate commit/PR on each repository)
  • A single location to find all code, rather than having to search across repositories when e.g. debugging
  • Easier to contribute for the first time, no need to clone and work on multiple repositories in parallel
  • Development/maintenance of packages in the context of an application can be easier if the packages are in the same repository as the application, e.g. when working on plugins for a CMS
  • Centralization of communication: Issues, PRs, docs, etc. on a single repository

Limitations of Composer and Packagist.org

There are a few problems that prevent Composer from supporting the installation of multiple packages from a single VCS repository. Composer would need the ability to install different versions of the packages in the repository alongside each other. When installing packages from a Git repository, for example, you cannot checkout different versions for different subdirectories. This means that you would need an entire copy of the repository for each installed package from the repository. Installing these would require large amounts of unnecessary disk space and potentially traffic. Even when installing ZIP archives, they aren't available from the existing download locations per subdirectory, requiring Composer to download a copy of the entire code including all other packages in the monorepo for each package.

That's also the reason why Packagist.org does not offer a way to install packages which are not stored at the root of their repository: Packagist.org does not currently host the code archives for download but simply references their source, e.g. GitHub. Generating and hosting zip files of subdirectories would be a massive undertaking in terms of infrastructure, bandwidth and operations.

Multipackages on Private Packagist

If you wish to use a monorepo with packages in subdirectories of your repository for the advantages outlined above, you can use a Private Packagist multipackage to install the individual Composer packages in another project. Multipackages were first added to Private Packagist with Security Monitoring in July, 2020. You need to have a composer.json file in each directory which should be considered a package of its own.

When adding a package by URL you have the option to select "Repository contains multiple packages or composer.json is located in a subdirectory". After selecting the option you can enter a glob pattern to decide which composer.json files should be treated as the root directory for separate packages, defaulting to all composer.json files in any directory. You have the option to exclude specific composer.json files which you do not want to turn into packages.

Add Package dialog with multipackage options expanded

Packages created from repository subdirectories with this method can only be installed from dist. That means Composer will always download and extract a zip file for the package, never clone the repository, due to the limitations described above. All other Private Packagist features work on multipackages like on any other package. For example, we'll monitor your composer.lock files in subdirectories for security issues in your dependencies and notify you.

The ACME CMS package and its 3 plugins from subdirectories in Private Packagist with composer.lock scanned for security issues

Installation of these packages in a different project now works the same as any other package from Private Packagist. You can require them by name as long as your organization's Private Packagist URL is listed as a repository in composer.json

composer.json
{
    "repositories": [
        {"type": "composer", "url": "https://repo.packagist.com/acme/"},
        {"packagist.org": false}
    ]
}
composer require acme/plugin-cache

Sign Up for a Free Trial on packagist.com

Get access to multipackages for your monorepos and many more Composer tools like third party repository mirroring, security monitoring or separate Composer subrepositories for clients.