How we Automated Shopsys Packages Release from 2 days to 1 Console Command

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!

The Manual Shopsys Release Process

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...

Before Release Stage

Release Stage

After Release Stage

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.

From Human Check-list to Command with Workers

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.

Shopsys Release Process Now?

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!