Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public function __construct()
$this->safeAddCommand(new Commands\PreviousLogs);
$this->safeAddCommand(new Commands\Cloud);
$this->safeAddCommand(new Commands\Tinker);
$this->safeAddCommand(new Commands\SecretCreate);
}

public function safeAddCommand(Command $command): ?Command
Expand Down
42 changes: 42 additions & 0 deletions src/BrefCloudClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ class BrefCloudClient
private const STAGING_URL = 'https://staging.bref.cloud';
private const LOCAL_URL = 'http://localhost:8000';

public const AWS_REGIONS = [
'us-east-1',
'us-east-2',
'us-west-1',
'us-west-2',
'eu-west-1',
'eu-west-2',
'eu-west-3',
'eu-central-1',
'eu-north-1',
'ap-northeast-1',
'ap-northeast-2',
'ap-northeast-3',
'ap-south-1',
'ap-southeast-1',
'ap-southeast-2',
'ca-central-1',
'sa-east-1',
];

public readonly string $url;
private HttpClientInterface $client;

Expand Down Expand Up @@ -161,6 +181,7 @@ public function markDeploymentFinished(
* url: string|null,
* outputs: array<string, string>,
* app: array{id: int, name: string},
* aws_account_id: int|null,
* }
*
* @throws HttpExceptionInterface
Expand All @@ -179,6 +200,7 @@ public function getEnvironment(int $id): array
* url: string|null,
* outputs: array<string, string>,
* app: array{id: int, name: string},
* aws_account_id: int|null,
* }
*
* @throws HttpExceptionInterface
Expand Down Expand Up @@ -276,4 +298,24 @@ public function addAwsAccount(mixed $teamId, string $accountName, string $roleAr
],
]);
}

public function createSecret(int $teamId, string $appName, string $environmentName, string $name, string $value, ?int $awsAccountId = null, ?string $region = null): void
{
$body = [
'team_id' => $teamId,
'app_name' => $appName,
'environment_name' => $environmentName,
'name' => $name,
'value' => $value,
];
if ($awsAccountId !== null) {
$body['aws_account_id'] = $awsAccountId;
}
if ($region !== null) {
$body['region'] = $region;
}
$this->client->request('POST', '/api/v1/secrets', [
'json' => $body,
]);
}
}
9 changes: 1 addition & 8 deletions src/Commands/Deploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,7 @@ private function selectAwsRegion(): string
{
$region = IO::ask(new ChoiceQuestion(
'Please select the AWS region to deploy to:',
[
'us-east-1',
'us-east-2',
'eu-west-1',
'eu-west-2',
'eu-west-3',
// @TODO: regions
],
BrefCloudClient::AWS_REGIONS,
));

if (! is_string($region)) {
Expand Down
138 changes: 138 additions & 0 deletions src/Commands/SecretCreate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php declare(strict_types=1);

namespace Bref\Cli\Commands;

use Bref\Cli\BrefCloudClient;
use Bref\Cli\Cli\IO;
use Exception;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;

class SecretCreate extends ApplicationCommand
{
protected function configure(): void
{
$this
->setName('secret:create')
->setDescription('Create a secret for an environment')
->addArgument('name', InputArgument::OPTIONAL, 'The secret name')
->addArgument('value', InputArgument::OPTIONAL, 'The secret value')
->addOption('app', null, InputOption::VALUE_REQUIRED, 'The app name (if no config file exists)');

parent::configure();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
// If --app and --team are provided, we can skip loading the config file
if ($input->getOption('app') && $input->getOption('team')) {
$appName = $input->getOption('app');
$team = $input->getOption('team');
$environment = $input->getOption('env');
} else {
[
'appName' => $appName,
'environmentName' => $environment,
'team' => $team,
] = $this->parseStandardOptions($input);

// Override app name if --app option provided
if ($input->getOption('app')) {
$appName = $input->getOption('app');
}
}

// Get secret name (from argument or prompt)
$name = $input->getArgument('name');
if (empty($name)) {
$question = new Question('Secret name: ');
$question->setValidator(function (?string $value): string {
if (empty($value)) {
throw new Exception('Secret name cannot be empty');
}
return $value;
});
$name = IO::ask($question);
}

// Get secret value (from argument or prompt)
$value = $input->getArgument('value');
if ($value === null) {
$question = new Question('Secret value: ');
$question->setHidden(true)->setHiddenFallback(false);
$value = IO::ask($question);
}

$brefCloud = new BrefCloudClient;

// Resolve team slug to team ID
$teams = $brefCloud->listTeams();
$teamId = null;
foreach ($teams as $t) {
if ($t['slug'] === $team) {
$teamId = $t['id'];
break;
}
}
if ($teamId === null) {
throw new Exception("Team '$team' not found");
}

// Check if the environment exists and is deployed
$awsAccountId = null;
$region = null;
try {
$existingEnv = $brefCloud->findEnvironment($team, $appName, $environment);
if (! empty($existingEnv['region'])) {
// Environment is deployed, use its aws_account_id and region
$awsAccountId = $existingEnv['aws_account_id'];
$region = $existingEnv['region'];
}
} catch (HttpExceptionInterface $e) {
if ($e->getResponse()->getStatusCode() !== 404) {
throw $e;
}
IO::verbose('Environment not found: ' . $e->getMessage());
// Environment doesn't exist
}

if ($awsAccountId === null) {
// Environment doesn't exist or isn't deployed, need to select AWS account and region
$awsAccounts = $brefCloud->listAwsAccounts();
if (empty($awsAccounts)) {
throw new Exception('No AWS accounts found. Please connect an AWS account first using "bref connect".');
}
$awsAccountsByName = [];
foreach ($awsAccounts as $account) {
$awsAccountsByName[$account['name']] = $account['id'];
}
$awsAccountName = IO::ask(new ChoiceQuestion(
'Select the AWS account:',
array_keys($awsAccountsByName),
));
if (! is_string($awsAccountName)) {
throw new Exception('No AWS account selected');
}
$awsAccountId = $awsAccountsByName[$awsAccountName];

$region = IO::ask(new ChoiceQuestion(
'Select the AWS region:',
BrefCloudClient::AWS_REGIONS,
));
if (! is_string($region)) {
throw new Exception('No region selected');
}
}

IO::spin('Creating secret');
$brefCloud->createSecret($teamId, $appName, $environment, $name, $value, $awsAccountId, $region);
IO::spinSuccess('Secret created');

return 0;
}
}