Skip to content

Commit cbe501e

Browse files
authored
Merge pull request #44 from brefphp/secret-create
Add 'secret:create' command
2 parents e10e927 + fabd080 commit cbe501e

File tree

4 files changed

+182
-8
lines changed

4 files changed

+182
-8
lines changed

src/Application.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public function __construct()
3232
$this->safeAddCommand(new Commands\PreviousLogs);
3333
$this->safeAddCommand(new Commands\Cloud);
3434
$this->safeAddCommand(new Commands\Tinker);
35+
$this->safeAddCommand(new Commands\SecretCreate);
3536
}
3637

3738
public function safeAddCommand(Command $command): ?Command

src/BrefCloudClient.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,26 @@ class BrefCloudClient
1313
private const STAGING_URL = 'https://staging.bref.cloud';
1414
private const LOCAL_URL = 'http://localhost:8000';
1515

16+
public const AWS_REGIONS = [
17+
'us-east-1',
18+
'us-east-2',
19+
'us-west-1',
20+
'us-west-2',
21+
'eu-west-1',
22+
'eu-west-2',
23+
'eu-west-3',
24+
'eu-central-1',
25+
'eu-north-1',
26+
'ap-northeast-1',
27+
'ap-northeast-2',
28+
'ap-northeast-3',
29+
'ap-south-1',
30+
'ap-southeast-1',
31+
'ap-southeast-2',
32+
'ca-central-1',
33+
'sa-east-1',
34+
];
35+
1636
public readonly string $url;
1737
private HttpClientInterface $client;
1838

@@ -161,6 +181,7 @@ public function markDeploymentFinished(
161181
* url: string|null,
162182
* outputs: array<string, string>,
163183
* app: array{id: int, name: string},
184+
* aws_account_id: int|null,
164185
* }
165186
*
166187
* @throws HttpExceptionInterface
@@ -179,6 +200,7 @@ public function getEnvironment(int $id): array
179200
* url: string|null,
180201
* outputs: array<string, string>,
181202
* app: array{id: int, name: string},
203+
* aws_account_id: int|null,
182204
* }
183205
*
184206
* @throws HttpExceptionInterface
@@ -276,4 +298,24 @@ public function addAwsAccount(mixed $teamId, string $accountName, string $roleAr
276298
],
277299
]);
278300
}
301+
302+
public function createSecret(int $teamId, string $appName, string $environmentName, string $name, string $value, ?int $awsAccountId = null, ?string $region = null): void
303+
{
304+
$body = [
305+
'team_id' => $teamId,
306+
'app_name' => $appName,
307+
'environment_name' => $environmentName,
308+
'name' => $name,
309+
'value' => $value,
310+
];
311+
if ($awsAccountId !== null) {
312+
$body['aws_account_id'] = $awsAccountId;
313+
}
314+
if ($region !== null) {
315+
$body['region'] = $region;
316+
}
317+
$this->client->request('POST', '/api/v1/secrets', [
318+
'json' => $body,
319+
]);
320+
}
279321
}

src/Commands/Deploy.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,7 @@ private function selectAwsRegion(): string
216216
{
217217
$region = IO::ask(new ChoiceQuestion(
218218
'Please select the AWS region to deploy to:',
219-
[
220-
'us-east-1',
221-
'us-east-2',
222-
'eu-west-1',
223-
'eu-west-2',
224-
'eu-west-3',
225-
// @TODO: regions
226-
],
219+
BrefCloudClient::AWS_REGIONS,
227220
));
228221

229222
if (! is_string($region)) {

src/Commands/SecretCreate.php

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Bref\Cli\Commands;
4+
5+
use Bref\Cli\BrefCloudClient;
6+
use Bref\Cli\Cli\IO;
7+
use Exception;
8+
use Symfony\Component\Console\Input\InputArgument;
9+
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Console\Input\InputOption;
11+
use Symfony\Component\Console\Output\OutputInterface;
12+
use Symfony\Component\Console\Question\ChoiceQuestion;
13+
use Symfony\Component\Console\Question\Question;
14+
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
15+
16+
class SecretCreate extends ApplicationCommand
17+
{
18+
protected function configure(): void
19+
{
20+
$this
21+
->setName('secret:create')
22+
->setDescription('Create a secret for an environment')
23+
->addArgument('name', InputArgument::OPTIONAL, 'The secret name')
24+
->addArgument('value', InputArgument::OPTIONAL, 'The secret value')
25+
->addOption('app', null, InputOption::VALUE_REQUIRED, 'The app name (if no config file exists)');
26+
27+
parent::configure();
28+
}
29+
30+
protected function execute(InputInterface $input, OutputInterface $output): int
31+
{
32+
// If --app and --team are provided, we can skip loading the config file
33+
if ($input->getOption('app') && $input->getOption('team')) {
34+
$appName = $input->getOption('app');
35+
$team = $input->getOption('team');
36+
$environment = $input->getOption('env');
37+
} else {
38+
[
39+
'appName' => $appName,
40+
'environmentName' => $environment,
41+
'team' => $team,
42+
] = $this->parseStandardOptions($input);
43+
44+
// Override app name if --app option provided
45+
if ($input->getOption('app')) {
46+
$appName = $input->getOption('app');
47+
}
48+
}
49+
50+
// Get secret name (from argument or prompt)
51+
$name = $input->getArgument('name');
52+
if (empty($name)) {
53+
$question = new Question('Secret name: ');
54+
$question->setValidator(function (?string $value): string {
55+
if (empty($value)) {
56+
throw new Exception('Secret name cannot be empty');
57+
}
58+
return $value;
59+
});
60+
$name = IO::ask($question);
61+
}
62+
63+
// Get secret value (from argument or prompt)
64+
$value = $input->getArgument('value');
65+
if ($value === null) {
66+
$question = new Question('Secret value: ');
67+
$question->setHidden(true)->setHiddenFallback(false);
68+
$value = IO::ask($question);
69+
}
70+
71+
$brefCloud = new BrefCloudClient;
72+
73+
// Resolve team slug to team ID
74+
$teams = $brefCloud->listTeams();
75+
$teamId = null;
76+
foreach ($teams as $t) {
77+
if ($t['slug'] === $team) {
78+
$teamId = $t['id'];
79+
break;
80+
}
81+
}
82+
if ($teamId === null) {
83+
throw new Exception("Team '$team' not found");
84+
}
85+
86+
// Check if the environment exists and is deployed
87+
$awsAccountId = null;
88+
$region = null;
89+
try {
90+
$existingEnv = $brefCloud->findEnvironment($team, $appName, $environment);
91+
if (! empty($existingEnv['region'])) {
92+
// Environment is deployed, use its aws_account_id and region
93+
$awsAccountId = $existingEnv['aws_account_id'];
94+
$region = $existingEnv['region'];
95+
}
96+
} catch (HttpExceptionInterface $e) {
97+
if ($e->getResponse()->getStatusCode() !== 404) {
98+
throw $e;
99+
}
100+
IO::verbose('Environment not found: ' . $e->getMessage());
101+
// Environment doesn't exist
102+
}
103+
104+
if ($awsAccountId === null) {
105+
// Environment doesn't exist or isn't deployed, need to select AWS account and region
106+
$awsAccounts = $brefCloud->listAwsAccounts();
107+
if (empty($awsAccounts)) {
108+
throw new Exception('No AWS accounts found. Please connect an AWS account first using "bref connect".');
109+
}
110+
$awsAccountsByName = [];
111+
foreach ($awsAccounts as $account) {
112+
$awsAccountsByName[$account['name']] = $account['id'];
113+
}
114+
$awsAccountName = IO::ask(new ChoiceQuestion(
115+
'Select the AWS account:',
116+
array_keys($awsAccountsByName),
117+
));
118+
if (! is_string($awsAccountName)) {
119+
throw new Exception('No AWS account selected');
120+
}
121+
$awsAccountId = $awsAccountsByName[$awsAccountName];
122+
123+
$region = IO::ask(new ChoiceQuestion(
124+
'Select the AWS region:',
125+
BrefCloudClient::AWS_REGIONS,
126+
));
127+
if (! is_string($region)) {
128+
throw new Exception('No region selected');
129+
}
130+
}
131+
132+
IO::spin('Creating secret');
133+
$brefCloud->createSecret($teamId, $appName, $environment, $name, $value, $awsAccountId, $region);
134+
IO::spinSuccess('Secret created');
135+
136+
return 0;
137+
}
138+
}

0 commit comments

Comments
 (0)