Do you release open-source? Do you have monorepo? Do you release over 10 monorepo packages at once? Do you still do it manually per package just to be sure?
Good news, it's not a single day-task, but this whole process can be automated. Let's me show how we did it in Shopsys.
Monorepo release management has ~~few~~ many gotchas. The one that requires most of your attention I described in Monorepo Composer Magic post. 1 missed commit or forgotten version change in composer.json
of your package and you've just released a broken package!
I mean the ugly kind of errors you'll find out only after someone tries to install the package. Of course, you can improve this by 3-layer monorepo tests, but there is still a 50 % chance for human error.
Let's get practical!
Shopsys is an open-source e-commerce build on Symfony. I helped them with monorepo, Symfony and open-source standards setup through 2018.
When we first looked at release todo-document, it had roughly 17 steps. After a bit deeper look we realized some steps have actually 5-10 more steps in it. In the end, we found over 40 steps in 3 various stages.
Just to give you the idea...
composer.json
dependencies of each packageCHANGELOG.md
since the previous releaseUPGRADE.md
next-version-dev
next-version-dev
dev
Do you want to check them all? Just see this directory on Github.
Shopsys releases new version every month and they had to do all these steps manually. Until now. Automation of this process saves time and attention of 3 developers (2 for code-review), that could be used for new features.
No surprise here, that the final pull-request got a bit out of hand...
It took 49 days and 3 900 new lines to get the PR merged. Why? Well, when you introduce simple automatization of a complex process, people start to see how easy is to convert manual daunting work to a new PHP class that does the work for them. So more and more ideas came.
To automate the process, we used MonorepoBuilder, resp. it's release-flow feature.
composer require symplify/monorepo-builder
The implements a worker for each step described above. Workers are grouped by stage and ordered by priority, so the whole process is under control.
<?php declare(strict_types=1);
namespace Utils\Release\ReleaseWorker;
use Nette\Utils\DateTime;
use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use Symplify\MonorepoBuilder\Release\Contract\ReleaseWorker;
use PharIo\Version\Version;
final class UpdateChangelogToDateReleaseWorker implements ReleaseWorkerInterface
{
/**
* 1 line description of what this worker does, in a commanding form! e.g.:
* - "Add new tag"
* - "Dump new items to CHANGELOG.md"
* - "Run coding standards"
*/
public function getDescription(Version $version): string
{
return 'Update CHANGELOG.md "Unreleased" to version and today date';
}
/**
* Higher first
*/
public function getPriority(): int
{
return 1000;
}
public function work(Version $version): void
{
$changelogPath = getcwd() . '/CHANGELOG.md';
$content = FileSystem::read($changelogPath);
// before: ## Unreleased
// after: ## v7.0.0-beta6 - 2019-02-18
$newContent = Strings::replace(
$content,
'#^\#\#Unreleased$#',
'## ' . $version->getVersionString() . ' - ' . DateTime::from('today')->format('Y-m-d')
);
FileSystem::write($changelogPath, $newContent);
}
}
Each step is written this way.
Do you want to include stages? We did, so the worker implemented Symplify\MonorepoBuilder\Release\Contract\ReleaseWorker\StageAwareInterface
.
In these workers, you can trigger Travis CI with API, use Packagist API to check new version are released... sky it the limit.
vendor/bin/monorepo-builder release v7.0.0-beta6 --stage release-candidate
# → review...
vendor/bin/monorepo-builder release v7.0.0-beta6 --stage release
# → CI work + 2nd review...
vendor/bin/monorepo-builder release v7.0.0-beta6 --stage after-release
Complex process made simple with PHP! ✅
Btw, do you know how Symplify 14-package monorepo release process looks like?
vendor/bin/monorepo-builder release v5.4.1
Just with bare MonorepoBuilder install.
How does your package release process look like?
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!