Skip to content

Commit a048deb

Browse files
authored
Merge pull request #47 from weierophinney/hotfix/fix-changelog-operations
Idempotent operations for reading, checkout branch prior to writing
2 parents 4cc1b00 + 00a391d commit a048deb

11 files changed

+293
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ All notable changes to this project will be documented in this file, in reverse
2222

2323
### Fixed
2424

25-
- Nothing.
25+
- [#47](https://github.com/laminas/automatic-releases/pull/47) fixes `CHANGELOG.md` update operations to avoid preventable failures during the release process.
2626

2727
## 1.2.1 - 2020-08-12
2828

bin/console.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Laminas\AutomaticReleases\Application\Command\ReleaseCommand;
1414
use Laminas\AutomaticReleases\Application\Command\SwitchDefaultBranchToNextMinor;
1515
use Laminas\AutomaticReleases\Changelog\BumpAndCommitChangelogVersionViaKeepAChangelog;
16+
use Laminas\AutomaticReleases\Changelog\ChangelogExistsViaConsole;
1617
use Laminas\AutomaticReleases\Changelog\CommitReleaseChangelogViaKeepAChangelog;
1718
use Laminas\AutomaticReleases\Environment\EnvironmentVariables;
1819
use Laminas\AutomaticReleases\Git\CheckoutBranchViaConsole;
@@ -69,16 +70,24 @@ static function (int $errorCode, string $message = '', string $file = '', int $l
6970
$httpClient,
7071
$githubToken
7172
));
73+
$changelogExists = new ChangelogExistsViaConsole();
7274
$checkoutBranch = new CheckoutBranchViaConsole();
7375
$commit = new CommitFileViaConsole();
7476
$push = new PushViaConsole();
75-
$commitChangelog = new CommitReleaseChangelogViaKeepAChangelog(new SystemClock(), $commit, $push, $logger);
77+
$commitChangelog = new CommitReleaseChangelogViaKeepAChangelog(
78+
new SystemClock(),
79+
$changelogExists,
80+
$checkoutBranch,
81+
$commit,
82+
$push,
83+
$logger
84+
);
7685
$createCommitText = new CreateReleaseTextThroughChangelog(JwageGenerateChangelog::create(
7786
$makeRequests,
7887
$httpClient
7988
));
8089
$createReleaseText = new ConcatenateMultipleReleaseTexts([
81-
new CreateReleaseTextViaKeepAChangelog(),
90+
new CreateReleaseTextViaKeepAChangelog($changelogExists),
8291
$createCommitText,
8392
]);
8493
$createRelease = new CreateReleaseThroughApiCall(
@@ -87,6 +96,7 @@ static function (int $errorCode, string $message = '', string $file = '', int $l
8796
$githubToken
8897
);
8998
$bumpChangelogVersion = new BumpAndCommitChangelogVersionViaKeepAChangelog(
99+
$changelogExists,
90100
$checkoutBranch,
91101
$commit,
92102
$push,

src/Changelog/BumpAndCommitChangelogVersionViaKeepAChangelog.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use Psr\Log\LoggerInterface;
1414
use Webmozart\Assert\Assert;
1515

16-
use function file_exists;
1716
use function sprintf;
1817

1918
class BumpAndCommitChangelogVersionViaKeepAChangelog implements BumpAndCommitChangelogVersion
@@ -26,21 +25,24 @@ class BumpAndCommitChangelogVersionViaKeepAChangelog implements BumpAndCommitCha
2625
Updates the %s file to add a changelog entry for a new %s version.
2726
COMMIT;
2827

28+
private ChangelogExists $changelogExists;
2929
private CheckoutBranch $checkoutBranch;
3030
private CommitFile $commitFile;
3131
private Push $push;
3232
private LoggerInterface $logger;
3333

3434
public function __construct(
35+
ChangelogExists $changelogExists,
3536
CheckoutBranch $checkoutBranch,
3637
CommitFile $commitFile,
3738
Push $push,
3839
LoggerInterface $logger
3940
) {
40-
$this->checkoutBranch = $checkoutBranch;
41-
$this->commitFile = $commitFile;
42-
$this->push = $push;
43-
$this->logger = $logger;
41+
$this->changelogExists = $changelogExists;
42+
$this->checkoutBranch = $checkoutBranch;
43+
$this->commitFile = $commitFile;
44+
$this->push = $push;
45+
$this->logger = $logger;
4446
}
4547

4648
public function __invoke(
@@ -49,16 +51,16 @@ public function __invoke(
4951
SemVerVersion $version,
5052
BranchName $sourceBranch
5153
): void {
52-
($this->checkoutBranch)($repositoryDirectory, $sourceBranch);
53-
54-
$changelogFile = sprintf('%s/%s', $repositoryDirectory, self::CHANGELOG_FILE);
55-
if (! file_exists($changelogFile)) {
54+
if (! ($this->changelogExists)($sourceBranch, $repositoryDirectory)) {
5655
// No changelog
5756
$this->logger->info('BumpAndCommitChangelog: No CHANGELOG.md file detected');
5857

5958
return;
6059
}
6160

61+
($this->checkoutBranch)($repositoryDirectory, $sourceBranch);
62+
63+
$changelogFile = sprintf('%s/%s', $repositoryDirectory, self::CHANGELOG_FILE);
6264
$versionString = $version->fullReleaseName();
6365
$bumper = new ChangelogBump($changelogFile);
6466
$newVersion = $bumper->$bumpType($versionString);

src/Changelog/ChangelogExists.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laminas\AutomaticReleases\Changelog;
6+
7+
use Laminas\AutomaticReleases\Git\Value\BranchName;
8+
9+
interface ChangelogExists
10+
{
11+
/**
12+
* @param non-empty-string $repositoryDirectory
13+
*/
14+
public function __invoke(
15+
BranchName $sourceBranch,
16+
string $repositoryDirectory
17+
): bool;
18+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laminas\AutomaticReleases\Changelog;
6+
7+
use Laminas\AutomaticReleases\Git\Value\BranchName;
8+
use Symfony\Component\Process\Process;
9+
10+
class ChangelogExistsViaConsole implements ChangelogExists
11+
{
12+
/**
13+
* @param non-empty-string $repositoryDirectory
14+
*/
15+
public function __invoke(
16+
BranchName $sourceBranch,
17+
string $repositoryDirectory
18+
): bool {
19+
$process = new Process(['git', 'show', $sourceBranch->name() . ':CHANGELOG.md'], $repositoryDirectory);
20+
$process->run();
21+
22+
return $process->isSuccessful();
23+
}
24+
}

src/Changelog/CommitReleaseChangelogViaKeepAChangelog.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Laminas\AutomaticReleases\Changelog;
66

7+
use Laminas\AutomaticReleases\Git\CheckoutBranch;
78
use Laminas\AutomaticReleases\Git\CommitFile;
89
use Laminas\AutomaticReleases\Git\Push;
910
use Laminas\AutomaticReleases\Git\Value\BranchName;
@@ -17,7 +18,6 @@
1718
use Symfony\Component\Console\Output\NullOutput;
1819
use Webmozart\Assert\Assert;
1920

20-
use function file_exists;
2121
use function sprintf;
2222

2323
final class CommitReleaseChangelogViaKeepAChangelog implements CommitReleaseChangelog
@@ -31,20 +31,26 @@ final class CommitReleaseChangelogViaKeepAChangelog implements CommitReleaseChan
3131
COMMIT;
3232

3333
private Clock $clock;
34+
private ChangelogExists $changelogExists;
35+
private CheckoutBranch $checkoutBranch;
3436
private CommitFile $commitFile;
3537
private Push $push;
3638
private LoggerInterface $logger;
3739

3840
public function __construct(
3941
Clock $clock,
42+
ChangelogExists $changelogExists,
43+
CheckoutBranch $checkoutBranch,
4044
CommitFile $commitFile,
4145
Push $push,
4246
LoggerInterface $logger
4347
) {
44-
$this->clock = $clock;
45-
$this->commitFile = $commitFile;
46-
$this->push = $push;
47-
$this->logger = $logger;
48+
$this->clock = $clock;
49+
$this->changelogExists = $changelogExists;
50+
$this->checkoutBranch = $checkoutBranch;
51+
$this->commitFile = $commitFile;
52+
$this->push = $push;
53+
$this->logger = $logger;
4854
}
4955

5056
/**
@@ -55,16 +61,18 @@ public function __invoke(
5561
SemVerVersion $version,
5662
BranchName $sourceBranch
5763
): void {
58-
$changelogFile = sprintf('%s/%s', $repositoryDirectory, self::CHANGELOG_FILE);
59-
if (! file_exists($changelogFile)) {
64+
if (! ($this->changelogExists)($sourceBranch, $repositoryDirectory)) {
6065
// No changelog
6166
$this->logger->info('No CHANGELOG.md file detected');
6267

6368
return;
6469
}
6570

71+
$changelogFile = sprintf('%s/%s', $repositoryDirectory, self::CHANGELOG_FILE);
6672
$versionString = $version->fullReleaseName();
6773

74+
($this->checkoutBranch)($repositoryDirectory, $sourceBranch);
75+
6876
if (! $this->updateChangelog($changelogFile, $versionString)) {
6977
// Failure to update; nothing to commit
7078
return;

src/Github/CreateReleaseTextViaKeepAChangelog.php

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,25 @@
55
namespace Laminas\AutomaticReleases\Github;
66

77
use InvalidArgumentException;
8+
use Laminas\AutomaticReleases\Changelog\ChangelogExists;
89
use Laminas\AutomaticReleases\Git\Value\BranchName;
910
use Laminas\AutomaticReleases\Git\Value\SemVerVersion;
1011
use Laminas\AutomaticReleases\Github\Api\GraphQL\Query\GetMilestoneChangelog\Response\Milestone;
1112
use Laminas\AutomaticReleases\Github\Value\RepositoryName;
1213
use Phly\KeepAChangelog\Common\ChangelogParser;
1314
use Phly\KeepAChangelog\Exception\ExceptionInterface;
15+
use Symfony\Component\Process\Process;
1416
use Webmozart\Assert\Assert;
1517

16-
use function file_exists;
17-
use function file_get_contents;
18-
1918
class CreateReleaseTextViaKeepAChangelog implements CreateReleaseText
2019
{
20+
private ChangelogExists $changelogExists;
21+
22+
public function __construct(ChangelogExists $changelogExists)
23+
{
24+
$this->changelogExists = $changelogExists;
25+
}
26+
2127
public function __invoke(
2228
Milestone $milestone,
2329
RepositoryName $repositoryName,
@@ -27,7 +33,7 @@ public function __invoke(
2733
): string {
2834
$changelog = (new ChangelogParser())
2935
->findChangelogForVersion(
30-
file_get_contents($repositoryDirectory . '/CHANGELOG.md'),
36+
$this->fetchChangelogContentsFromBranch($sourceBranch, $repositoryDirectory),
3137
$semVerVersion->fullReleaseName()
3238
);
3339

@@ -43,15 +49,14 @@ public function canCreateReleaseText(
4349
BranchName $sourceBranch,
4450
string $repositoryDirectory
4551
): bool {
46-
$changelogFile = $repositoryDirectory . '/CHANGELOG.md';
47-
if (! file_exists($changelogFile)) {
52+
if (! ($this->changelogExists)($sourceBranch, $repositoryDirectory)) {
4853
return false;
4954
}
5055

5156
try {
5257
$changelog = (new ChangelogParser())
5358
->findChangelogForVersion(
54-
file_get_contents($changelogFile),
59+
$this->fetchChangelogContentsFromBranch($sourceBranch, $repositoryDirectory),
5560
$semVerVersion->fullReleaseName()
5661
);
5762

@@ -62,4 +67,21 @@ public function canCreateReleaseText(
6267
return false;
6368
}
6469
}
70+
71+
/**
72+
* @psalm-param non-empty-string $repositoryDirectory
73+
* @psalm-return non-empty-string
74+
*/
75+
private function fetchChangelogContentsFromBranch(
76+
BranchName $sourceBranch,
77+
string $repositoryDirectory
78+
): string {
79+
$process = new Process(['git', 'show', $sourceBranch->name() . ':CHANGELOG.md'], $repositoryDirectory);
80+
$process->mustRun();
81+
82+
$contents = $process->getOutput();
83+
Assert::notEmpty($contents);
84+
85+
return $contents;
86+
}
6587
}

test/unit/Changelog/BumpAndCommitChangelogVersionViaKeepAChangelogTest.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Laminas\AutomaticReleases\Changelog\BumpAndCommitChangelogVersion;
88
use Laminas\AutomaticReleases\Changelog\BumpAndCommitChangelogVersionViaKeepAChangelog;
9+
use Laminas\AutomaticReleases\Changelog\ChangelogExists;
10+
use Laminas\AutomaticReleases\Changelog\ChangelogExistsViaConsole;
911
use Laminas\AutomaticReleases\Git\CheckoutBranch;
1012
use Laminas\AutomaticReleases\Git\CommitFile;
1113
use Laminas\AutomaticReleases\Git\Push;
@@ -44,6 +46,7 @@ protected function setUp(): void
4446
$this->push = $this->createMock(Push::class);
4547
$this->logger = $this->createMock(LoggerInterface::class);
4648
$this->bumpAndCommitChangelog = new BumpAndCommitChangelogVersionViaKeepAChangelog(
49+
new ChangelogExistsViaConsole(),
4750
$this->checkoutBranch,
4851
$this->commitFile,
4952
$this->push,
@@ -58,12 +61,8 @@ public function testReturnsEarlyWhenNoChangelogFilePresent(): void
5861
$version = SemVerVersion::fromMilestoneName('1.0.1');
5962

6063
$this->checkoutBranch
61-
->expects($this->once())
62-
->method('__invoke')
63-
->with(
64-
$this->equalTo($repoDir),
65-
$sourceBranch
66-
);
64+
->expects($this->never())
65+
->method('__invoke');
6766

6867
$this->logger
6968
->expects($this->once())
@@ -118,6 +117,13 @@ public function testAddsNewReleaseVersionUsingBumpTypeToChangelogFileAndCommitsA
118117

119118
Assert::stringNotEmpty($repoDir);
120119

120+
$changelogExists = $this->createMock(ChangelogExists::class);
121+
$changelogExists
122+
->expects($this->once())
123+
->method('__invoke')
124+
->with($sourceBranch, $repoDir)
125+
->willReturn(true);
126+
121127
$this->logger
122128
->expects($this->once())
123129
->method('info')
@@ -154,8 +160,16 @@ public function testAddsNewReleaseVersionUsingBumpTypeToChangelogFileAndCommitsA
154160
->with(
155161
);
156162

163+
$bumpAndCommitChangelog = new BumpAndCommitChangelogVersionViaKeepAChangelog(
164+
$changelogExists,
165+
$this->checkoutBranch,
166+
$this->commitFile,
167+
$this->push,
168+
$this->logger
169+
);
170+
157171
$this->assertNull(
158-
($this->bumpAndCommitChangelog)(
172+
$bumpAndCommitChangelog(
159173
$bumpType,
160174
$repoDir,
161175
$version,
@@ -191,6 +205,13 @@ private function createMockChangelog(): string
191205

192206
file_put_contents($changelogFile, self::CHANGELOG_STUB);
193207

208+
(new Process(['git', 'init', '.'], $repo))->mustRun();
209+
(new Process(['git', 'config', 'user.email', '[email protected]'], $repo))->mustRun();
210+
(new Process(['git', 'config', 'user.name', 'Just Me'], $repo))->mustRun();
211+
(new Process(['git', 'add', '.'], $repo))->mustRun();
212+
(new Process(['git', 'commit', '-m', 'Initial import'], $repo))->mustRun();
213+
(new Process(['git', 'switch', '-c', '1.0.x'], $repo))->mustRun();
214+
194215
return $changelogFile;
195216
}
196217

0 commit comments

Comments
 (0)