Skip to content

Commit 3d0ab34

Browse files
authored
Merge pull request #25 from contentful/feat/auto-release
Automate release process
2 parents 4535807 + 9a24c50 commit 3d0ab34

File tree

4 files changed

+229
-2
lines changed

4 files changed

+229
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55

66
## [Unreleased](https://github.com/contentful/contentful-core.php/compare/2.1.1...HEAD)
77

8+
<!-- PENDING-CHANGES -->
9+
> No meaningful changes since last release.
10+
<!-- /PENDING-CHANGES -->
11+
812
## [2.1.1](https://github.com/contentful/contentful-core.php/tree/2.1.1) (2018-11-08)
913

1014
### Fixed

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727
"psr-4": {
2828
"Contentful\\Tests\\Core\\": "tests"
2929
},
30-
"files": ["scripts/TestCase.php"]
30+
"files": [
31+
"scripts/TestCase.php"
32+
]
3133
},
3234
"scripts": {
3335
"test": "vendor/bin/phpunit",
3436
"test-quick-fail": "php vendor/bin/phpunit --stop-on-error --stop-on-failure -v",
3537
"lint-syntax": "$([ -f php-cs-fixer.phar ] && echo \"php php-cs-fixer.phar\" || echo \"php-cs-fixer\") fix --dry-run --stop-on-violation -v",
36-
"lint-static-analysis": "$([ -f phpstan.phar ] && echo \"php phpstan.phar\" || echo \"phpstan\") analyse --level=max src/"
38+
"lint-static-analysis": "$([ -f phpstan.phar ] && echo \"php phpstan.phar\" || echo \"phpstan\") analyse --level=max src/",
39+
"release": "php scripts/release.php"
3740
},
3841
"extra": {
3942
"branch-alias": {

scripts/ReleaseUpdater.php

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the contentful/contentful-core package.
5+
*
6+
* @copyright 2015-2018 Contentful GmbH
7+
* @license MIT
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
class ReleaseUpdater
13+
{
14+
/**
15+
* @var string
16+
*/
17+
const PENDING_CHANGES_START = '<!-- PENDING-CHANGES -->';
18+
19+
/**
20+
* @var string
21+
*/
22+
const PENDING_CHANGES_END = '<!-- /PENDING-CHANGES -->';
23+
24+
/**
25+
* @var string
26+
*/
27+
const PENDING_CHANGES_PLACEHOLDER = '> No meaningful changes since last release.';
28+
29+
/**
30+
* @var string
31+
*/
32+
const UNRELEASED_CHANGES_HEADER = '## [Unreleased](https://github.com/contentful/contentful-core.php/compare/%s...HEAD)';
33+
34+
/**
35+
* @var string
36+
*/
37+
const NEW_RELEASE_HEADER = '## [%s](https://github.com/contentful/contentful-core.php/tree/%s) (%s)';
38+
39+
/**
40+
* @var string
41+
*/
42+
private $changelogPath;
43+
44+
/**
45+
* @var string
46+
*/
47+
private $composerConfigPath;
48+
49+
public function __construct(string $changelogPath, string $composerConfigPath)
50+
{
51+
$this->changelogPath = $changelogPath;
52+
$this->composerConfigPath = $composerConfigPath;
53+
}
54+
55+
public function updateChangelog(string $version)
56+
{
57+
$content = \file_get_contents($this->changelogPath);
58+
59+
$pendingChangesStart = \mb_strpos($content, self::PENDING_CHANGES_START);
60+
$pendingChangesEnd = \mb_strpos($content, self::PENDING_CHANGES_END);
61+
62+
if (\false === $pendingChangesStart || \false === $pendingChangesEnd) {
63+
throw new Exception('ERROR: Cannot reliably determine the changelog section to edit, aborting.');
64+
}
65+
66+
$changelog = $this->createUpdatedChangelog($version, $content, $pendingChangesStart, $pendingChangesEnd);
67+
68+
\file_put_contents($this->changelogPath, $changelog);
69+
}
70+
71+
private function createUpdatedChangelog(string $version, string $content, int $contentStart, int $contentEnd): string
72+
{
73+
$extractFrom = $contentStart + \mb_strlen(self::PENDING_CHANGES_START);
74+
$extractLength = $contentEnd - $contentStart - \mb_strlen(self::PENDING_CHANGES_START);
75+
76+
$newReleaseChanges = \trim(\mb_substr($content, $extractFrom, $extractLength));
77+
78+
$changelogStart = \trim(\mb_substr($content, 0, $contentStart));
79+
$changelogEnd = \trim(\mb_substr($content, $contentEnd + \ mb_strlen(self::PENDING_CHANGES_END)));
80+
81+
$unreleasedLineStart = \mb_strpos($changelogStart, '## [Unreleased]');
82+
$changelogStart = \trim(\mb_substr($changelogStart, 0, $unreleasedLineStart));
83+
84+
$changelogTemplate = '[START]
85+
86+
[UNRELEASED_HEADER]
87+
88+
[PENDING_CHANGES_START]
89+
[PENDING_CHANGES_PLACEHOLDER]
90+
[PENDING_CHANGES_END]
91+
92+
[NEW_RELEASE_HEADER]
93+
94+
[NEW_RELEASE_CHANGES]
95+
96+
[END]
97+
';
98+
99+
$tempPath = \tempnam(\sys_get_temp_dir(), 'changelog-');
100+
\file_put_contents($tempPath, $newReleaseChanges);
101+
\system('pbcopy < '.$tempPath);
102+
103+
return \strtr($changelogTemplate, [
104+
'[START]' => $changelogStart,
105+
'[UNRELEASED_HEADER]' => \sprintf(self::UNRELEASED_CHANGES_HEADER, $version),
106+
'[PENDING_CHANGES_START]' => self::PENDING_CHANGES_START,
107+
'[PENDING_CHANGES_PLACEHOLDER]' => self::PENDING_CHANGES_PLACEHOLDER,
108+
'[PENDING_CHANGES_END]' => self::PENDING_CHANGES_END,
109+
'[NEW_RELEASE_HEADER]' => \sprintf(self::NEW_RELEASE_HEADER, $version, $version, \date('Y-m-d')),
110+
'[NEW_RELEASE_CHANGES]' => $newReleaseChanges,
111+
'[END]' => $changelogEnd,
112+
]);
113+
}
114+
115+
public function updateComposerAliasVersion(string $composerAliasVersion)
116+
{
117+
$json = \file_get_contents($this->composerConfigPath);
118+
$contents = \json_decode($json, \true);
119+
120+
$contents['extra']['branch-alias']['dev-master'] = $composerAliasVersion.'-dev';
121+
122+
\file_put_contents(
123+
$this->composerConfigPath,
124+
\json_encode($contents, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)."\n"
125+
);
126+
}
127+
}

scripts/release.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the contentful/contentful-core package.
5+
*
6+
* @copyright 2015-2018 Contentful GmbH
7+
* @license MIT
8+
*/
9+
10+
declare(strict_types=1);
11+
12+
require __DIR__.'/ReleaseUpdater.php';
13+
14+
function exit_message(string $message, int $status)
15+
{
16+
message($message);
17+
exit($status);
18+
}
19+
20+
function message(string $message = '')
21+
{
22+
echo '## '.$message."\n";
23+
}
24+
25+
if (!isset($argv[1])) {
26+
exit_message('ERROR: Cannot proceed without a valid release version, aborting.', 1);
27+
}
28+
29+
$version = $argv[1];
30+
$pattern = '/^(?<version>[0-9]+\.[0-9]+\.[0-9]+)(?<prerelease>-[0-9a-zA-Z.]+)?(?<build>\+[0-9a-zA-Z.]+)?$/';
31+
if (!\preg_match($pattern, $version)) {
32+
exit_message('ERROR: Provided version is not a valid semver identifier, aborting.', 1);
33+
}
34+
35+
$composerAliasVersion = \null;
36+
if (isset($argv[2])) {
37+
$composerAliasVersion = $argv[2];
38+
$pattern = '/^(?<version>[0-9]+\.[0-9]+\.[0-9]+)$/';
39+
if (!\preg_match($pattern, $composerAliasVersion)) {
40+
exit_message('ERROR: Provided master alias version is not a valid semver identifier, aborting.', 1);
41+
}
42+
}
43+
44+
$changelogPath = \getcwd().'/CHANGELOG.md';
45+
$composerConfigPath = \getcwd().'/composer.json';
46+
47+
try {
48+
$updater = new ReleaseUpdater($changelogPath, $composerConfigPath);
49+
$updater->updateChangelog($version);
50+
if (\null !== $composerAliasVersion) {
51+
$updater->updateComposerAliasVersion($composerAliasVersion);
52+
}
53+
} catch (\Exception $exception) {
54+
exit_message($exception->getMessage(), 1);
55+
}
56+
57+
message();
58+
message('Committing changes to changelog');
59+
\system('git add '.$changelogPath);
60+
\shell_exec('git commit --message="chore(release): Prepare version '.$version.'"');
61+
message('Changes committed!');
62+
message();
63+
64+
message('Creating git tag');
65+
\shell_exec('git tag --sign '.$version.' --message="'.$version.'"');
66+
message('Tag created!');
67+
message();
68+
69+
if (\null !== $composerAliasVersion) {
70+
message('Committing changes to composer.json');
71+
\shell_exec('git add '.$composerConfigPath);
72+
\shell_exec('git commit --message="chore(release): Prepare development of next version"');
73+
message('Changes committed!');
74+
message();
75+
}
76+
77+
message();
78+
79+
$url = \shell_exec('git remote get-url origin');
80+
$url = \str_replace('github.com:', 'github.com/', $url);
81+
$url = 'https://'.\mb_substr($url, 4, -5).'/releases/tag/'.$version;
82+
83+
message('Release created!');
84+
message('Execute the following command to push to Github:');
85+
message();
86+
message(' git push --follow-tags');
87+
message();
88+
message('Then open the Github page at this URL:');
89+
message();
90+
message(' '.$url);
91+
message();
92+
message('and then update the Github release, the content is already copied in your clipboard!');
93+
message();

0 commit comments

Comments
 (0)