Managing Composer Repositories via the Command Line
I’m in the process of a top-to-bottom restructure of this site: everything from the design, to the code architecture, to the content, to the deployment strategies. One major piece of this work has been integrating the Composer-based workflow I’m so familiar with using on the large enterprise client sites that I’ve worked on into my personal toolset.
Composer is an amazing tool for PHP, and if you aren’t using it yet for WordPress development, I highly encourage you to stop reading this post right now and go check it out. I’ve given a talk about it at several different WordCamps in the past two years, and there’s lots of great content on the web about what it does and how to integrated it into your workflow.
What I want to write about today, though, is a very small part of Composer: the config command. Or, more specifically, the config command as it pertains to non-Packagist based repositories, and how you might consider structuring your composer.json file going forward.
Manually Adding Repositories
Let’s imagine for a minute that you started reading this blog post, didn’t know a thing about Composer, and that you went and studied up on it before coming back to this blog post. First, thank you for your dedication! Second, you probably learned the following bits of information:
- The
composer require
command, by default, visits Packagist.org to find an instance of repositories that match the name you provided. So, if you rancomposer require wp-cli/wp-cli-bundle --dev
, you’d get a copy of the full WP-CLI tool and all of its bundled commands, because it has a reference on Packagist. - All packages require a valid composer.json file with, at a minimum, a name entry in order to be installable (well, sort of, but I’ll talk about that later).
- You are required to give your local Composer project more information if you need to install packages which are not referenced on Packagist.
Assuredly, you learned a ton more information than this. But, in terms of installing a package, those are the key details you need to know: if it’s not on Packagist, you need to tell Composer where to find it.
How?
Here’s an example
Let’s say you wanted to install anything that’s in the WordPress.org plugins repository. This is the bare minimum you will need in your composer.json file:
{
"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
}
]
}
This block of JSON says to Composer, “Hey, the dependencies for my project are located both on Packagist.org and on WPackagist.org, which is a Composer-based mirror of the WordPress plugins repository. Once that’s in your composer.json file, you’re free to install any plugin in the WordPress repository by using the custom prefixes that WPackagist uses. For instance, if I wanted to install Jetpack, I’d run the following command:
composer require wpackagist-plugin/jetpack
I’d automatically get the latest version. Note, though, that this example is a little contrived, because Automattic wisely contributes Jetpack to Packagist, so the additional repository is not needed, but not every company or developer does this. You could run composer require automattic/jetpack
instead and get the same result.
Another Example
Let’s say you’re like me, and you’re working on a full rebuild of your site. Maybe you’re making a new theme, or a new plugin, or both, and are not yet ready to put your repository on Packagist (or, you’d argue that you don’t need to). You can add the URL to your Git repository right in your composer.json file:
{
"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
},
{
"type": "git",
"url": "https://github.com/jmichaelward/board-game-collector.git"
}
]
}
In this case, if I run composer require jmichaelward/board-game-collector:dev-deve[email protected]
, I’ll get the latest version of my Board Game Collector plugin’s develop branch. You might start to see why this is useful.
Automatically Adding Repositories
A Small Problem
There’s something that the Composer documentation and online content that I’ve seen which talks about it doesn’t necessarily cover.
First, there’s no requirement which states that a package’s name needs to be the same as its repository. In the previous example, my repository is at jmichaelward/board-game-collector, but my Composer package’s name could just as easily be jmw/bgc. I can’t reliable use the repositories list to cross-reference the packages in my require object.
Second, there’s a Composer command that lets you add repositories. Consider this example:
composer config repositories.0 git https://github.com/jmichaelward/board-game-collector.git
This command automatically updates the repositories array in my composer.json file! No more need to open my composer.json anymore, I can just achieve this update directly from the command line, right?
Well, not exactly. Note this section of the command: repositories.0. That says, “set the 0 index of my array to this value”, where “git” is the type of repository, and “https://github.com/jmichaelward/board-game-collector.git” is the URL. If I already had a zero index, such as the WPackagist repository in the earlier examples, it would get overwritten.
A Solution, and a Proposal
The composer config command will let you create a key-value object instead. Consider this alternative:
composer config repositories.jmichaelward/board-game-collector git https://github.com/jmichaelward/board-game-collector.git
This converts the repositories array in your composer.json to an object, where each key is the name you provided in the command (in this case, jmichaelward/board-game-collector), and the values are the type and URL for that repository.
If we use the command line to add all of our repositories, we wind up with something that looks more like this:
{
"repositories": {
"wpackagist": {
"type": "composer",
"url": "https://wpackagist.org"
},
"jmichaelward/board-game-collector": {
"type": "git",
"url": "https://github.com/jmichaelward/board-game-collector.git"
}
}
}
The benefit here is two-fold: first, we gain access to the repositories we need to install the packages our project requires. But second, and more importantly, we get a direct map between the package names in our require block and the repositories they’re linked to.
This second benefit could not be overstated. By setting your repositories up as a key-value object instead of an array, you help other engineers who want to quickly look at the repositories for your project’s dependencies. They can easily see which packages map to which URLs, then access those URLs directly. It’s a small documentation touch, and something that I’m looking to do more and more of in my future projects.
Summary
Composer is an amazing tool for managing PHP project dependencies, but sometimes the documentation isn’t the greatest, or best practices aren’t well-defined. Documentation is one of the most important parts of building any projects, as it can help others who are looking at your code in the future, or it can even help your future self as you question what you did many weeks or months ago.
Composer’s config command provides a lot of flexibility for setting up your composer.json file. I highly recommend giving it a look and learning other ways it can help you streamline your workflow when managing dependencies.