Lines 95.23% 20 / 21
Functions and Methods 80.00% 4 / 5
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
ReleaseScript 95.23% 20 / 21 80.00% 4 / 5 9 0.00% 0 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 prepareLatestRelease 100.00% 1 / 1 100.00% 1 / 1 1
 findCheckFailure 100.00% 8 / 8 100.00% 1 / 1 4
 stageRelease 0.00% 0 / 1 0.00% 0 / 1 2
 runEntrypoint 100.00% 10 / 10 100.00% 1 / 1 2
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Runtime\Broker\Deploy;
6
7use Amp\Cancellation;
8use Amp\CancelledException;
9use Stewart\Runtime\Config\GitDeployConfig;
10use Stewart\Runtime\Exception\DeployError;
11use Stewart\Runtime\Exception\DeployException;
12
13final readonly class ReleaseScript
14{
15    private const string ENTRYPOINT = 'stewart-entrypoint';
16
17    private const string UNEXPLAINED_CHECK_FAILURE = 'stewart check failed without saying why';
18
19    public function __construct(
20        private ExternalCommandRunner $commands,
21        private GitDeployConfig $gitDeploy,
22        private ?string $releaseCheckConfigFile,
23    ) {}
24
25    /**
26     * @throws DeployException
27     * @throws CancelledException
28     */
29    public function prepareLatestRelease(Cancellation $cancellation): PreparedRelease
30    {
31        return PreparedRelease::fromScriptOutput($this->runEntrypoint(['release-prepare'], $cancellation)->output);
32    }
33
34    /**
35     * @throws DeployException
36     * @throws CancelledException
37     */
38    public function findCheckFailure(CommitId $commit, Cancellation $cancellation): ?string
39    {
40        $arguments = ['release-check', $commit->value];
41
42        if ($this->releaseCheckConfigFile !== null) {
43            $arguments[] = '--config=' . $this->releaseCheckConfigFile;
44        }
45
46        $result = $this->commands->runCommand([self::ENTRYPOINT, ...$arguments], $this->gitDeploy->prepareTimeout, $cancellation);
47
48        if ($result->isSuccessful()) {
49            return null;
50        }
51
52        $reason = $result->findFirstErrorLine();
53
54        return $reason === '' ? self::UNEXPLAINED_CHECK_FAILURE : $reason;
55    }
56
57    /**
58     * @throws DeployException
59     * @throws CancelledException
60     */
61    public function stageRelease(CommitId $commit, Cancellation $cancellation): void
62    {
63        $this->runEntrypoint(['release-stage', $commit->value], $cancellation);
64    }
65
66    /**
67     * @param list<string> $arguments
68     * @throws DeployException
69     * @throws CancelledException
70     */
71    private function runEntrypoint(array $arguments, Cancellation $cancellation): ExternalCommandResult
72    {
73        $command = [self::ENTRYPOINT, ...$arguments];
74        $result = $this->commands->runCommand($command, $this->gitDeploy->prepareTimeout, $cancellation);
75
76        if (!$result->isSuccessful()) {
77            throw DeployException::createForReason(
78                DeployError::CommandFailed,
79                command: implode(' ', $command),
80                exitCode: $result->exitCode,
81                detail: $result->findLastErrorLine(),
82            );
83        }
84
85        return $result;
86    }
87}