Skip to content

Commit 12dde9b

Browse files
authored
Merge pull request #230 from ghostwriter/feature/support-running-automatic-releases-actions-after-failure
Re-running automatic releases actions after failure
2 parents 3589c9f + 1a77168 commit 12dde9b

File tree

7 files changed

+210
-13
lines changed

7 files changed

+210
-13
lines changed

bin/console.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Laminas\AutomaticReleases\Git\CreateTagViaConsole;
2525
use Laminas\AutomaticReleases\Git\FetchAndSetCurrentUserByReplacingCurrentOriginRemote;
2626
use Laminas\AutomaticReleases\Git\GetMergeTargetCandidateBranchesFromRemoteBranches;
27+
use Laminas\AutomaticReleases\Git\HasTagViaConsole;
2728
use Laminas\AutomaticReleases\Git\PushViaConsole;
2829
use Laminas\AutomaticReleases\Github\Api\GraphQL\Query\GetMilestoneFirst100IssuesAndPullRequests;
2930
use Laminas\AutomaticReleases\Github\Api\GraphQL\RunGraphQLQuery;
@@ -127,7 +128,7 @@ static function (int $errorCode, string $message = '', string $file = '', int $l
127128
$getMilestone,
128129
$commitChangelog,
129130
$createReleaseText,
130-
new CreateTagViaConsole(),
131+
new CreateTagViaConsole(new HasTagViaConsole(), $logger),
131132
$push,
132133
$createRelease,
133134
),

src/Git/CreateTag.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
interface CreateTag
1111
{
12-
/** @param non-empty-string $repositoryDirectory */
12+
/**
13+
* @param non-empty-string $repositoryDirectory
14+
* @param non-empty-string $tagName
15+
*/
1316
public function __invoke(
1417
string $repositoryDirectory,
1518
BranchName $sourceBranch,

src/Git/CreateTagViaConsole.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,43 @@
1010
use Psl\File;
1111
use Psl\Filesystem;
1212
use Psl\Shell;
13+
use Psr\Log\LoggerInterface;
14+
15+
use function sprintf;
1316

1417
final class CreateTagViaConsole implements CreateTag
1518
{
19+
public function __construct(
20+
private readonly HasTag $hasTag,
21+
private readonly LoggerInterface $logger,
22+
) {
23+
}
24+
1625
public function __invoke(
1726
string $repositoryDirectory,
1827
BranchName $sourceBranch,
1928
string $tagName,
2029
string $changelog,
2130
SecretKeyId $keyId,
2231
): void {
32+
if (($this->hasTag)($repositoryDirectory, $tagName)) {
33+
$this->logger->info(
34+
sprintf('[CreateTagViaConsole] Skipping this step; tag "%s" already exists.', $tagName),
35+
);
36+
37+
return;
38+
}
39+
2340
$tagFileName = Filesystem\create_temporary_file(Env\temp_dir(), 'created_tag');
2441

2542
File\write($tagFileName, $changelog);
2643

2744
Shell\execute('git', ['checkout', $sourceBranch->name()], $repositoryDirectory);
28-
Shell\execute('git', ['tag', $tagName, '-F', $tagFileName, '--cleanup=whitespace', '--local-user=' . $keyId->id()], $repositoryDirectory);
45+
46+
Shell\execute(
47+
'git',
48+
['tag', $tagName, '-F', $tagFileName, '--cleanup=whitespace', '--local-user=' . $keyId->id()],
49+
$repositoryDirectory,
50+
);
2951
}
3052
}

src/Git/HasTag.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laminas\AutomaticReleases\Git;
6+
7+
interface HasTag
8+
{
9+
/**
10+
* @param non-empty-string $repositoryDirectory
11+
* @param non-empty-string $tagName
12+
*/
13+
public function __invoke(
14+
string $repositoryDirectory,
15+
string $tagName,
16+
): bool;
17+
}

src/Git/HasTagViaConsole.php

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\Git;
6+
7+
use Psl\Shell;
8+
9+
use function str_contains;
10+
use function trim;
11+
12+
final class HasTagViaConsole implements HasTag
13+
{
14+
public function __invoke(string $repositoryDirectory, string $tagName): bool
15+
{
16+
$output = Shell\execute('git', ['tag', '--list', $tagName], $repositoryDirectory);
17+
18+
if (trim($output) === '') {
19+
return false;
20+
}
21+
22+
return str_contains($output, $tagName);
23+
}
24+
}

test/unit/Git/CreateTagViaConsoleTest.php

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace Laminas\AutomaticReleases\Test\Unit\Git;
66

77
use Laminas\AutomaticReleases\Git\CreateTagViaConsole;
8+
use Laminas\AutomaticReleases\Git\HasTag;
9+
use Laminas\AutomaticReleases\Git\HasTagViaConsole;
810
use Laminas\AutomaticReleases\Git\Value\BranchName;
911
use Laminas\AutomaticReleases\Gpg\ImportGpgKeyFromStringViaTemporaryFile;
1012
use Laminas\AutomaticReleases\Gpg\SecretKeyId;
@@ -13,8 +15,10 @@
1315
use Psl\Filesystem;
1416
use Psl\Shell;
1517
use Psr\Http\Message\UriInterface;
18+
use Psr\Log\LoggerInterface;
1619

1720
use function Psl\File\read;
21+
use function sprintf;
1822

1923
/** @covers \Laminas\AutomaticReleases\Git\CreateTagViaConsole */
2024
final class CreateTagViaConsoleTest extends TestCase
@@ -46,19 +50,24 @@ protected function setUp(): void
4650

4751
public function testCreatesSignedTag(): void
4852
{
53+
$logger = $this->createMock(LoggerInterface::class);
54+
$logger->expects(self::never())->method('info');
55+
4956
$sourceUri = $this->createMock(UriInterface::class);
57+
$sourceUri->method('__toString')->willReturn($this->repository);
5058

51-
$sourceUri->method('__toString')
52-
->willReturn($this->repository);
59+
$hasTag = $this->createMock(HasTag::class);
60+
$hasTag->method('__invoke')
61+
->with($this->repository, 'name-of-the-tag')
62+
->willReturn(false);
5363

54-
(new CreateTagViaConsole())
55-
->__invoke(
56-
$this->repository,
57-
BranchName::fromName('tag-branch'),
58-
'name-of-the-tag',
59-
'changelog text for the tag',
60-
$this->key,
61-
);
64+
(new CreateTagViaConsole($hasTag, $logger))(
65+
$this->repository,
66+
BranchName::fromName('tag-branch'),
67+
'name-of-the-tag',
68+
'changelog text for the tag',
69+
$this->key,
70+
);
6271

6372
Shell\execute('git', ['tag', '-v', 'name-of-the-tag'], $this->repository);
6473

@@ -69,4 +78,46 @@ public function testCreatesSignedTag(): void
6978
self::assertStringContainsString('a commit', $fetchedTag);
7079
self::assertStringContainsString('-----BEGIN PGP SIGNATURE-----', $fetchedTag);
7180
}
81+
82+
public function testSkipsIfTagAlreadyExists(): void
83+
{
84+
$tagName = 'name-of-the-tag';
85+
$logger = $this->createMock(LoggerInterface::class);
86+
$logger->expects(self::never())
87+
->method('info');
88+
89+
$sourceUri = $this->createMock(UriInterface::class);
90+
$sourceUri->method('__toString')->willReturn($this->repository);
91+
92+
$hasTag = new HasTagViaConsole();
93+
94+
(new CreateTagViaConsole($hasTag, $logger))(
95+
$this->repository,
96+
BranchName::fromName('tag-branch'),
97+
$tagName,
98+
'changelog text for the tag',
99+
$this->key,
100+
);
101+
102+
Shell\execute('git', ['tag', '-v', $tagName], $this->repository);
103+
$fetchedTag = Shell\execute('git', ['show', $tagName], $this->repository);
104+
105+
self::assertStringContainsString('tag name-of-the-tag', $fetchedTag);
106+
self::assertStringContainsString('changelog text for the tag', $fetchedTag);
107+
self::assertStringContainsString('a commit', $fetchedTag);
108+
self::assertStringContainsString('-----BEGIN PGP SIGNATURE-----', $fetchedTag);
109+
110+
$logger = $this->createMock(LoggerInterface::class);
111+
$logger->expects(self::once())
112+
->method('info')
113+
->with(sprintf('[CreateTagViaConsole] Skipping this step; tag "%s" already exists.', $tagName));
114+
115+
(new CreateTagViaConsole($hasTag, $logger))(
116+
$this->repository,
117+
BranchName::fromName('tag-branch'),
118+
$tagName,
119+
'changelog text for the tag',
120+
$this->key,
121+
);
122+
}
72123
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Laminas\AutomaticReleases\Test\Unit\Git;
6+
7+
use Laminas\AutomaticReleases\Git\HasTag;
8+
use Laminas\AutomaticReleases\Git\HasTagViaConsole;
9+
use PHPUnit\Framework\TestCase;
10+
use Psl\Env;
11+
use Psl\Filesystem;
12+
use Psl\Shell;
13+
14+
use function array_map;
15+
use function sprintf;
16+
17+
/** @covers \Laminas\AutomaticReleases\Git\HasTagViaConsole */
18+
final class HasTagViaConsoleTest extends TestCase
19+
{
20+
/** @var non-empty-string */
21+
private string $repository;
22+
private HasTag $hasTag;
23+
24+
protected function setUp(): void
25+
{
26+
parent::setUp();
27+
28+
$this->repository = Filesystem\create_temporary_file(Env\temp_dir(), 'HasTagViaConsoleRepository');
29+
30+
$this->hasTag = new HasTagViaConsole();
31+
32+
Filesystem\delete_file($this->repository);
33+
Filesystem\create_directory($this->repository);
34+
35+
Shell\execute('git', ['init'], $this->repository);
36+
37+
Shell\execute('git', ['config', 'user.email', '[email protected]'], $this->repository);
38+
Shell\execute('git', ['config', 'user.name', 'Just Me'], $this->repository);
39+
40+
array_map(fn (string $tag) => $this->createTag($tag), [
41+
'1.0.0',
42+
'2.0.0',
43+
]);
44+
}
45+
46+
private function createTag(string $tag): void
47+
{
48+
Shell\execute('git', ['commit', '--allow-empty', '-m', 'a commit for version ' . $tag], $this->repository);
49+
Shell\execute('git', ['tag', '-a', $tag, '-m', 'version ' . $tag], $this->repository);
50+
}
51+
52+
/**
53+
* @param non-empty-string $repository
54+
* @param non-empty-string $tag
55+
*/
56+
private function assertGitTagExists(string $repository, string $tag): void
57+
{
58+
self::assertTrue(($this->hasTag)($repository, $tag), sprintf('Failed asserting git tag "%s" exists.', $tag));
59+
}
60+
61+
/**
62+
* @param non-empty-string $repository
63+
* @param non-empty-string $tag
64+
*/
65+
private function assertGitTagMissing(string $repository, string $tag): void
66+
{
67+
self::assertFalse(($this->hasTag)($repository, $tag), sprintf('Failed asserting git tag "%s" is missing.', $tag));
68+
}
69+
70+
public function testHasTag(): void
71+
{
72+
$this->assertGitTagExists($this->repository, '1.0.0');
73+
}
74+
75+
public function testHasTagMissing(): void
76+
{
77+
$this->assertGitTagMissing($this->repository, '10.0.0');
78+
}
79+
}

0 commit comments

Comments
 (0)