Release Hooks ¶
The release command makes a new release for the current branch.
By itself this command already takes care of a number of requirements.
But sometimes you need to perform a special operation before and/or after the release process.
This might vary from updating the composer.json
branch-alias, to creating a pull request for
the new release.
Now, instead of doing this manually, HuPKit allows you to hook-into the release process, executing a custom callback before and/or after a new release is created.
## The Script
Add a PHP script named either pre-release.php
or post-release.php
at the root folder
of the _hubkit
configuration branch.
With the following contents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php
declare(strict_types=1);
use Psr\Container\ContainerInterface as Container;
use Rollerworks\Component\Version\Version;
return function (Container $container, Version $version, string $branch, ?string $releaseTitle, string $changelog) {
// Place the hooks logic here.
// The $container provides access to the HuPKit application Service Container
// with among following services: github, git (git.config, git.branch), process, filesystem, style, editor, logger.
//
// See \HubKit\Container for all services and there corresponding classes.
// Note: Only the services listed above are covered by the BC promise.
// !! CAUTION !!
//
// Hooks are loaded from a temporary location *outside of the repository*, use the `__DIR__`
// constant to load files related to the script, use `$container['current_dir']`
// to get the actual location to the project repository.
//
};
|
Caution
The hook is executed in the HuPKit application's context, you have full access to entire applications flow, anything that crashes the application cannot be resolved.
While possible, it's' best not to load external dependencies as there is
currently no promise this will work when HuPKit uses similar dependencies.
In this case it might be better to use the process
service to execute
a separate command.
Tip
HuPKit v2.0 will contain a complete plug-in architecture with more flexibility and dependency handling.
Examples ¶
Below you can find some examples how to use these hooks.
Updating the `composer.json` branch-alias (pre-release) ¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php
declare(strict_types=1);
// pre-release.php (in the _hubkit branch)
use Psr\Container\ContainerInterface as Container;
use Rollerworks\Component\Version\Version;
return function (Container $container, Version $version, string $branch, ?string $releaseTitle, string $changelog) {
$container->get('logger')->info('Updating composer branch-alias');
$container->get('process')->mustRun(['composer', 'config', 'extra.branch-alias.dev-'.$branch, sprintf('%d.%d-dev', $version->major, $version->minor)]);
// Caution: Make sure to commit the changes. HuPKit will refuse to continue if there are dangling changes.
/** @var \HubKit\Service\Git\GitBranch $gitBranch */
$gitBranch = $container->get('git.branch');
$gitBranch->add('composer.json');
$gitBranch->commit('Update composer branch-alias');
/** @var \HubKit\Service\Git $git */
$git = $container->get('git');
$git->pushToRemote('upstream', $branch);
};
|
Updating the `composer.json` branch-alias (post-release) ¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php
declare(strict_types=1);
// post-release.php (in the _hubkit branch)
use Psr\Container\ContainerInterface as Container;
use Rollerworks\Component\Version\Version;
return function (Container $container, Version $version, string $branch, ?string $releaseTitle, string $changelog) {
$container->get('logger')->info('Updating composer branch-alias');
$container->get('process')->mustRun(['composer', 'config', 'extra.branch-alias.dev-'.$branch, sprintf('%d.%d-dev', $version->major, $version->minor)]);
// Caution: Make sure to commit the changes. HubKit will refuse to continue if there are dangling changes.
/** @var \HubKit\Service\Git\GitBranch $gitBranch */
$gitBranch = $container->get('git.branch');
if ($gitBranch->isWorkingTreeReady()) {
return; // Nothing to commit, composer is already up-to-date
}
$gitBranch->add('composer.json');
$gitBranch->commit('Update composer branch-alias');
/** @var \HubKit\Service\Git $git */
$git = $container->get('git');
$git->pushToRemote('upstream', $branch);
};
|
### Creating a pull request for the release (pre-release) ¶
Important
This technique is not to be used as-is, understand the risk and be sure to apply enough error protections.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php
declare(strict_types=1);
// pre-release.php (in the _hubkit branch)
use Psr\Container\ContainerInterface as Container;
use Rollerworks\Component\Version\Version;
use Symfony\Component\Console\Helper\ProgressIndicator;
return function (Container $container, Version $version, string $branch, ?string $releaseTitle, string $changelog) {
/** @var \Psr\Log\LoggerInterface $logger */
$logger = $container->get('logger');
/** @var \HubKit\Service\GitHub $github */
$github = $container->get('github');
/** @var \HubKit\Service\Git\GitBranch $gitBranch */
$gitBranch = $container->get('git.branch');
/** @var \Symfony\Component\Console\Style\SymfonyStyle $style */
$style = $container->get('style');
$logger->info('Preparing new release branch');
$gitBranch->checkoutNew($releaseBranch = 'release-'.$version->full);
$logger->info('Updating composer branch-alias');
$container->get('process')->mustRun(['composer', 'config', 'extra.branch-alias.dev-'.$branch, sprintf('%d.%d-dev', $version->major, $version->minor)]);
// WARNING if nothing was changed this will crash the entire process, make sure to use something like:
//
// if ($gitBranch->isWorkingTreeReady()) {
// return;
// }
$gitBranch->add('composer.json');
$gitBranch->commit('Update composer branch-alias');
$gitBranch->pushToRemote('origin', $releaseBranch);
$pr = $github->openPullRequest($branch, $releaseBranch, 'Release v'.$version->full, 'This might be a good place for a changelog.');
$style->warning([
'Pull request '.$pr['html_url'].' was opened for this release.',
'The process will automatically continue once this pull request is merged.',
'!! DO NOT ABORT THE COMMAND !!'
]);
$progress = new ProgressIndicator($style);
$progress->start('Waiting for pull request to be merged.');
// Wait till the pull-request is merged. This might crash if the API limit is exceeded.
//
// Alternatively you can merge the pull request directly, but make sure you use a proper CI.
while ($github->getPullRequest($pr['number'])['state'] === 'open') {
sleep(30); // sleep for 30 seconds
$progress->advance();
}
if ($github->getPullRequest($pr['number'])['merged'] === false) {
$progress->finish('Pull request was closed. Aborting.');
exit(1);
}
$progress->finish('Pull request was merged, continuing.');
$gitBranch->pullRemote('upstream', $branch);
};
|
Final Words ¶
The post-release
script might be used to automatically publish a blog post,
or reset the version information in a PHP file to prepare for the next release.
For more advanced usage in either, the pre or post phase you might want to use something like https://github.com/liip/RMT#actions (be sure to set `vcs` to none to prevent conflicts).