Working assignment script
This commit is contained in:
parent
a44f49b91d
commit
f4bc3566aa
8 changed files with 1377 additions and 81 deletions
812
app/Console/Commands/SetGitlabCdVariables.php
Normal file
812
app/Console/Commands/SetGitlabCdVariables.php
Normal file
|
|
@ -0,0 +1,812 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\EnvironmentScope;
|
||||
use App\Lib\CdEnvVariable;
|
||||
use GrahamCampbell\GitLab\GitLabManager;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Psy\Readline\Hoa\ConsoleException;
|
||||
use SebastianBergmann\Diff\Differ;
|
||||
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
|
||||
|
||||
class SetGitlabCdVariables extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'uwil:gitlab:set_variables {--P|projectId=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = "Set default variables for an Uwil Project";
|
||||
|
||||
/**
|
||||
* The gitlab client
|
||||
*
|
||||
* @var \Gitlab\Client
|
||||
*/
|
||||
private \Gitlab\Client $client;
|
||||
|
||||
/**
|
||||
* The vars that should be present by default
|
||||
*
|
||||
* * These are type env_var.
|
||||
*
|
||||
* @var array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>>
|
||||
* Keyed with `\App\Enums\EnvironmentScope`.
|
||||
*/
|
||||
private static array $defaultEnvVars = [];
|
||||
|
||||
/**
|
||||
* Create a new console command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// * Init default vars reference
|
||||
if (empty(self::$defaultEnvVars)) {
|
||||
self::$defaultEnvVars = [
|
||||
'APP_DEBUG' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'APP_DEBUG',
|
||||
value: 'false',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'APP_DEBUG',
|
||||
value: 'false',
|
||||
protected: true,
|
||||
masked: false,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'APP_ENV' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'APP_ENV',
|
||||
value: 'staging',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'APP_ENV',
|
||||
value: 'production',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'APP_KEY' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'APP_KEY',
|
||||
value: $this->generateRandomKey(),
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
'APP_NAME' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'APP_NAME',
|
||||
value: 'ChangeMe',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
'DOMAIN' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'DOMAIN',
|
||||
value: '$CI_PROJECT_NAME.re7-s-web.fr',
|
||||
protected: true,
|
||||
raw: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'DOMAIN',
|
||||
value: 'changeme.changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'CERT_ISSUER' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'CERT_ISSUER',
|
||||
value: 're7-letsencrypt-production-dns01',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'CERT_ISSUER',
|
||||
value: 'letsencrypt-production-http01',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'DB_DATABASE' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'DB_DATABASE',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'DB_DATABASE',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'DB_USERNAME' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'DB_USERNAME',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'DB_USERNAME',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'DB_PASSWORD' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'DB_PASSWORD',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'DB_PASSWORD',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_ENCRYPTION' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'MAIL_ENCRYPTION',
|
||||
value: 'null',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'MAIL_ENCRYPTION',
|
||||
value: 'tls',
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_HOST' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'MAIL_HOST',
|
||||
value: 'null',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'MAIL_HOST',
|
||||
value: 'smtp-relay.brevo.com',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_PORT' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'MAIL_PORT',
|
||||
value: 'null',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'MAIL_PORT',
|
||||
value: '587',
|
||||
protected: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_USERNAME' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'MAIL_USERNAME',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'MAIL_USERNAME',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_PASSWORD' => [
|
||||
EnvironmentScope::staging->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::staging,
|
||||
key: 'MAIL_PASSWORD',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
EnvironmentScope::production->value => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::production,
|
||||
key: 'MAIL_PASSWORD',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
],
|
||||
'MAIL_FROM_ADDRESS' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'MAIL_FROM_ADDRESS',
|
||||
value: 'no-reply@changeme.changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
))->isProjectDefault(),
|
||||
'PVC_SIZE' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'PVC_SIZE',
|
||||
value: '1Gi',
|
||||
protected: true,
|
||||
)),
|
||||
'LDC_WEB_API_KEY' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'LDC_WEB_API_KEY',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
)),
|
||||
'SENTRY_LARAVEL_DSN' => (new CdEnvVariable(
|
||||
environment_scope: EnvironmentScope::all,
|
||||
key: 'SENTRY_LARAVEL_DSN',
|
||||
value: 'changeme',
|
||||
protected: true,
|
||||
masked: true,
|
||||
)),
|
||||
];
|
||||
self::$defaultEnvVars = Arr::sortRecursive(self::$defaultEnvVars);
|
||||
} //end if
|
||||
|
||||
$this->client = app(GitLabManager::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* ! This command may throw exceptions if there is a unhandled difference between actual vars and template.
|
||||
*
|
||||
* @return void
|
||||
* @throws \RuntimeException If an error occurs while comunicating with gitlab.
|
||||
* @throws \Psy\Readline\Hoa\ConsoleException If the user cancels this command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$this->warn(
|
||||
'! This command may throw exceptions if there is a unhandled difference between actual vars and template.'
|
||||
);
|
||||
$this->warn('!! Sensitive data is masked !!');
|
||||
|
||||
$projects = $this->client->projects();
|
||||
|
||||
$projectId = $this->option('projectId');
|
||||
/** @var int|null $projectId */
|
||||
$projectId = \is_numeric($projectId) ? intval($projectId) : null;
|
||||
|
||||
/** @var string|null */
|
||||
$projectNameWithNamespace = null;
|
||||
|
||||
if (is_null($projectId)) {
|
||||
$this->getInput(
|
||||
'Quel projet veut-on paramètrer ?',
|
||||
[
|
||||
'required',
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
function (
|
||||
string $parameter,
|
||||
string|null $value,
|
||||
callable $fail
|
||||
) use (
|
||||
$projects,
|
||||
&$projectId,
|
||||
&$projectNameWithNamespace
|
||||
) {
|
||||
$results = collect($projects->all(['search' => $value]));
|
||||
if (!$results->count()) {
|
||||
$fail('Project not found');
|
||||
return;
|
||||
}
|
||||
$projectNameWithNamespace = $results->count() > 1 ? $this->getChoice(
|
||||
'Veuillez choisir parmis les résultats',
|
||||
$results->pluck('name_with_namespace')->all()
|
||||
) : strval(Arr::get(Arr::get($results, 0), 'name_with_namespace'));
|
||||
|
||||
if (empty($projectNameWithNamespace)) {
|
||||
$fail('Project not found');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Selected project {$projectNameWithNamespace}");
|
||||
|
||||
$projectId = Arr::get($results->where(
|
||||
'name_with_namespace',
|
||||
$projectNameWithNamespace
|
||||
)->firstOrFail(), 'id');
|
||||
}
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$projectNameWithNamespace = Arr::get($this->client->projects()->show($projectId), 'name_with_namespace');
|
||||
|
||||
if (empty($projectNameWithNamespace)) {
|
||||
throw new \RuntimeException(
|
||||
"Could not get 'name_with_namespace' for project project id `{$projectId}`"
|
||||
);
|
||||
}
|
||||
} //end if
|
||||
|
||||
/** @var int $projectId */
|
||||
|
||||
$this->info("Using project id `{$projectId}` name `{$projectNameWithNamespace}`");
|
||||
|
||||
\sleep(6);
|
||||
|
||||
$actualProjectVars = Arr::sort($this->getProjectCdVars($projectId));
|
||||
|
||||
$newVars = Arr::sort($this->mergeVars(self::$defaultEnvVars, $actualProjectVars));
|
||||
|
||||
$this->showAndConfirmDiff($actualProjectVars, $newVars);
|
||||
|
||||
$this->warn('The following vars will be injected and replace previous CD vars');
|
||||
$this->line($this->stringifyVars($newVars));
|
||||
|
||||
if (!$this->confirm('Save these to git lab ?')) {
|
||||
throw new ConsoleException('Operation aborted');
|
||||
}
|
||||
|
||||
if (
|
||||
$this->getChoice(
|
||||
'Again: Previous vars will be lost and these will be saved, are you sure ?',
|
||||
['yes I am sure', 'no cancel please']
|
||||
) === 'no cancel please'
|
||||
) {
|
||||
throw new ConsoleException('Operation aborted');
|
||||
}
|
||||
|
||||
// * Remove existing vars before inserting all
|
||||
$this->removeExistingProjectCdVars($projectId);
|
||||
$this->saveProjectCdVarsToGitlab($projectId, $newVars);
|
||||
|
||||
$this->info('Done !!!');
|
||||
$this->info('Please check on ' . $this->getProjetCdVarsUrl($projectId));
|
||||
}
|
||||
|
||||
// * PRIVATE GITLAB
|
||||
|
||||
/**
|
||||
* Save compiled vars on Gitlab project
|
||||
*
|
||||
* @param integer $projectId
|
||||
* @param array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>> $newVars
|
||||
* @return void
|
||||
*/
|
||||
private function saveProjectCdVarsToGitlab(int $projectId, array $newVars): void
|
||||
{
|
||||
try {
|
||||
$updateProjectVar = function (int $projectId, CdEnvVariable $var) {
|
||||
$this->client->projects()->addVariable(
|
||||
$projectId,
|
||||
$var->getFormatedGitlabKey(),
|
||||
$var->value,
|
||||
$var->protected,
|
||||
$var->environment_scope->value,
|
||||
[
|
||||
'masked' => $var->masked,
|
||||
'raw' => $var->raw
|
||||
]
|
||||
);
|
||||
};
|
||||
foreach ($newVars as $varOrVarList) {
|
||||
if ($varOrVarList instanceof CdEnvVariable) {
|
||||
$updateProjectVar($projectId, $varOrVarList);
|
||||
continue;
|
||||
}
|
||||
foreach ($varOrVarList as $var) {
|
||||
$updateProjectVar($projectId, $var);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->warn(
|
||||
'Sorry an error just happened, please check actual env vars to prevent project crash ok k8s'
|
||||
);
|
||||
dump($e);
|
||||
exit;
|
||||
} //end try
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all existing project CD vars
|
||||
*
|
||||
* @param integer $projectId
|
||||
* @return void
|
||||
*/
|
||||
private function removeExistingProjectCdVars(int $projectId): void
|
||||
{
|
||||
collect($this->queryProjectCdVars($projectId))->each(function (array $gitlabVar) use ($projectId) {
|
||||
try {
|
||||
$this->client->projects()->removeVariable(
|
||||
$projectId,
|
||||
Arr::get($gitlabVar, 'key'),
|
||||
[
|
||||
'filter' => [
|
||||
'environment_scope' => Arr::get($gitlabVar, 'environment_scope')
|
||||
]
|
||||
]
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->warn(
|
||||
'Sorry an error just happened, please check actual env vars to prevent project crash ok k8s'
|
||||
);
|
||||
dump($e);
|
||||
exit;
|
||||
} //end try
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Project vars
|
||||
*
|
||||
* @param integer $projectId The project id for which we want cd variables.
|
||||
* @return array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>>
|
||||
*/
|
||||
private function getProjectCdVars(int $projectId): array
|
||||
{
|
||||
return Arr::sortRecursive(collect($this->queryProjectCdVars($projectId))
|
||||
->map(function (array $envVar) {
|
||||
$envVar = collect($envVar);
|
||||
$env_scope = EnvironmentScope::make($envVar->get('environment_scope', ''));
|
||||
if (!$env_scope) {
|
||||
throw new \Error('Unhandled scope \'' . $envVar->get('environment_scope', '') . '\'');
|
||||
}
|
||||
$envVar->put('environment_scope', $env_scope);
|
||||
return $envVar->all();
|
||||
})->filter(fn (array $envVar) => Arr::get($envVar, 'variable_type') === 'env_var')
|
||||
->map(fn (array $envVar) => CdEnvVariable::make($envVar))
|
||||
->groupBy(fn (CdEnvVariable $envVar) => $envVar->key, true)
|
||||
->map(function (Collection $envVars) {
|
||||
if ($envVars->count() === 1) {
|
||||
return $envVars->first();
|
||||
}
|
||||
return $envVars->values()
|
||||
->keyBy(fn (CdEnvVariable $envVar) => $envVar->environment_scope->value)
|
||||
->all();
|
||||
})->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Query Gitlab project vars
|
||||
*
|
||||
* @param integer $projectId
|
||||
* @return array
|
||||
*/
|
||||
private function queryProjectCdVars(int $projectId): array
|
||||
{
|
||||
// ! per_page max is 100, if this limit is reached need to implement pagination scrapping.
|
||||
return $this->client->projects()->variables($projectId, ['per_page' => 100]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project CD vars url
|
||||
*
|
||||
* @param integer $projectId
|
||||
* @return string
|
||||
*/
|
||||
private function getProjetCdVarsUrl(int $projectId): string
|
||||
{
|
||||
return Str::of(Arr::get($this->client->projects()->show($projectId), 'web_url'))
|
||||
->finish('/')->append('-/settings/ci_cd');
|
||||
}
|
||||
|
||||
// * PRIVATE MISC
|
||||
|
||||
/**
|
||||
* Show diff and request user consent
|
||||
*
|
||||
* @param array $actualProjectVars
|
||||
* @param array $newVars
|
||||
* @return void
|
||||
* @throws \Psy\Readline\Hoa\ConsoleException If user wants to abort.
|
||||
* @throws \RuntimeException If diff is empty.
|
||||
*/
|
||||
private function showAndConfirmDiff(array $actualProjectVars, array $newVars): void
|
||||
{
|
||||
// * Calc diff
|
||||
$builder = new UnifiedDiffOutputBuilder(
|
||||
// * custom header
|
||||
"--- Original\n+++ New\n",
|
||||
// * do not add line numbers to the diff
|
||||
false
|
||||
);
|
||||
$differ = new Differ($builder);
|
||||
$diff = Str::of($differ->diff(
|
||||
$this->stringifyVars(...$actualProjectVars),
|
||||
$this->stringifyVars(...$newVars),
|
||||
))->explode("\n");
|
||||
|
||||
$diff->each(function (string $line) {
|
||||
if (Str::of($line)->startsWith('-')) {
|
||||
$this->error($line);
|
||||
return;
|
||||
}
|
||||
if (Str::of($line)->startsWith('+')) {
|
||||
$this->info($line);
|
||||
return;
|
||||
}
|
||||
$this->line($line);
|
||||
});
|
||||
|
||||
if ($diff->count() <= 3) {
|
||||
throw new \RuntimeException(
|
||||
'There is no diff between skeletton Cd vars and actual vars, no need to update, Operation aborted'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->confirm('The following diff will be set, continue ?')) {
|
||||
throw new ConsoleException('Operation aborted');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify a var list
|
||||
*
|
||||
* @param \App\Lib\CdEnvVariable|array ...$vars
|
||||
* @return string
|
||||
*/
|
||||
private function stringifyVars(CdEnvVariable|array ...$vars): string
|
||||
{
|
||||
$out = Str::of('');
|
||||
foreach ($vars as $var) {
|
||||
if (is_array($var)) {
|
||||
$out = $out->append($this->stringifyVars(...$var));
|
||||
continue;
|
||||
}
|
||||
$out = $out->append($var)->newLine();
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join variables
|
||||
*
|
||||
* @param array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>> ...$cdVars
|
||||
* @return array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>>
|
||||
*/
|
||||
private function mergeVars(array ...$cdVars): array
|
||||
{
|
||||
/** @var array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>>|null */
|
||||
$firstArr = Arr::pull($cdVars, 0);
|
||||
if (!$firstArr) {
|
||||
return [];
|
||||
}
|
||||
collect($cdVars)->each(function (array $cdVarArray) use (&$firstArr) {
|
||||
/** @var array<string,\App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>>|null $cdVarArray */
|
||||
foreach ($cdVarArray as $key => $cdVarList) {
|
||||
/** @var \App\Lib\CdEnvVariable|array<string,\App\Lib\CdEnvVariable>|null The default config for actual key */
|
||||
$firstArrCdVarList = Arr::get($firstArr, $key);
|
||||
|
||||
// ? A default config exists for this key ?
|
||||
if (!$firstArrCdVarList) {
|
||||
// * Assign and continue
|
||||
Arr::set($firstArr, $key, $cdVarList);
|
||||
continue;
|
||||
}
|
||||
// * Need to merge props form here.
|
||||
// * This should indicate that environment_scope is all
|
||||
if ($firstArrCdVarList instanceof \App\Lib\CdEnvVariable) {
|
||||
$this->assertEnvironmentScopeIs(EnvironmentScope::all, ...Arr::wrap($cdVarList));
|
||||
|
||||
// ? Are we going to set all scopes var to an existing array of var with different scopes ?
|
||||
if (is_array($cdVarList)) {
|
||||
if ($firstArrCdVarList->environment_scope->equals(EnvironmentScope::all)) {
|
||||
Arr::set($firstArr, $key, $this->askWhichShouldBeKeptOnMergeScopes(
|
||||
$cdVarList,
|
||||
$firstArrCdVarList
|
||||
));
|
||||
continue;
|
||||
}
|
||||
Arr::set($firstArr, $key, $cdVarList);
|
||||
continue;
|
||||
}
|
||||
// * Replace firArrVar.
|
||||
Arr::set($firstArr, $key, $cdVarList);
|
||||
continue;
|
||||
} //end if
|
||||
|
||||
if ($cdVarList instanceof \App\Lib\CdEnvVariable) {
|
||||
if ($cdVarList->environment_scope->equals(EnvironmentScope::all)) {
|
||||
Arr::set($firstArr, $key, $this->askWhichShouldBeKeptOnMergeScopes(
|
||||
$firstArrCdVarList,
|
||||
$cdVarList
|
||||
));
|
||||
continue;
|
||||
}
|
||||
Arr::set($firstArr, $key, $cdVarList);
|
||||
continue;
|
||||
}
|
||||
|
||||
// * This should indicate that there is no environment_scope in array.
|
||||
$newCdVarList = $firstArrCdVarList;
|
||||
$this->assertEnvironmentScopeIsNot(EnvironmentScope::all, ...$cdVarList);
|
||||
foreach ($cdVarList as $environment_scope_value => $cdVar) {
|
||||
// ! The can merge all scope with other scopes
|
||||
$newCdVarList[$environment_scope_value] = $cdVar;
|
||||
}
|
||||
Arr::set($firstArr, $key, $newCdVarList);
|
||||
} //end foreach
|
||||
});
|
||||
return $firstArr;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array<string,\App\Lib\CdEnvVariable> $firstArrCdVarList
|
||||
* @param \App\Lib\CdEnvVariable $varWithAllScope
|
||||
* @return array<string,\App\Lib\CdEnvVariable>|CdEnvVariable
|
||||
*/
|
||||
private function askWhichShouldBeKeptOnMergeScopes(
|
||||
array $firstArrCdVarList,
|
||||
CdEnvVariable $varWithAllScope
|
||||
): array|CdEnvVariable {
|
||||
$this->info(' --- VAR ARRAY --->');
|
||||
collect($firstArrCdVarList)->each(fn (CdEnvVariable $var) => $this->info($var));
|
||||
$this->info(' --- VAR `All` scope --->');
|
||||
$this->info($varWithAllScope);
|
||||
|
||||
$message =
|
||||
'How do you want to proceed in order to merge `All` scope var into a multiple scopes var array ?';
|
||||
$replaceMessage = 'Replace the var array with the `All` scope var';
|
||||
$keepMessage = 'Replace the `All` scope var with var array';
|
||||
switch ($this->getChoice($message, [$replaceMessage, $keepMessage], $replaceMessage)) {
|
||||
case $replaceMessage:
|
||||
return $varWithAllScope;
|
||||
case $keepMessage:
|
||||
default:
|
||||
return $firstArrCdVarList;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asset that a variable `environment_scope` has a certain value.
|
||||
*
|
||||
* @param \App\Enums\EnvironmentScope $environment_scope
|
||||
* @param \App\Lib\CdEnvVariable ...$var
|
||||
* @return void
|
||||
* @throws \RuntimeException If the variable `environment_scope` is different that expected.
|
||||
*/
|
||||
private function assertEnvironmentScopeIs(
|
||||
EnvironmentScope $environment_scope,
|
||||
CdEnvVariable ...$var
|
||||
): void {
|
||||
foreach ($var as $cdVar) {
|
||||
if (!$cdVar->environment_scope->equals($environment_scope)) {
|
||||
$scopeLabel = $environment_scope->label();
|
||||
$this->error(' --- CONTEXT --->');
|
||||
collect($var)->each(fn (CdEnvVariable $var) => $this->info($var));
|
||||
$scopeLabel = $environment_scope->label();
|
||||
throw new \RuntimeException(
|
||||
"environment_scope should have been `{$scopeLabel}` into var {$cdVar}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asset that a variable `environment_scope` dow not have a certain value.
|
||||
*
|
||||
* @param \App\Enums\EnvironmentScope $environment_scope
|
||||
* @param \App\Lib\CdEnvVariable ...$var
|
||||
* @return void
|
||||
* @throws \RuntimeException If the variable `environment_scope` is different that expected.
|
||||
*/
|
||||
private function assertEnvironmentScopeIsNot(
|
||||
EnvironmentScope $environment_scope,
|
||||
CdEnvVariable ...$var
|
||||
): void {
|
||||
foreach ($var as $cdVar) {
|
||||
if ($cdVar->environment_scope->equals($environment_scope)) {
|
||||
$scopeLabel = $environment_scope->label();
|
||||
throw new \RuntimeException(
|
||||
"environment_scope shouldn't be `{$scopeLabel}` on var {$cdVar}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request user to make a choice
|
||||
*
|
||||
* @param string $askMessage
|
||||
* @param array<string> $choices
|
||||
* @param string|null $default
|
||||
* @return string
|
||||
*/
|
||||
private function getChoice(
|
||||
string $askMessage,
|
||||
array $choices,
|
||||
string $default = null
|
||||
): string {
|
||||
return $this->choice($askMessage, $choices, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user input
|
||||
*
|
||||
* @param string $askMessage Ask something to the user.
|
||||
* @param string|array $validate As you would pass to laravel validator.
|
||||
* @param string $errorMessage Message to show in case of wrong input.
|
||||
* @return string
|
||||
*/
|
||||
private function getInput(
|
||||
string $askMessage,
|
||||
string|array $validate = 'min:3|max:255',
|
||||
string $errorMessage = 'Invalid value try again'
|
||||
): string {
|
||||
$value = null;
|
||||
while (is_null($value)) {
|
||||
try {
|
||||
/** @var string|null $tmp */
|
||||
$tmp = $this->ask($askMessage);
|
||||
$validator = Validator::make(['input' => $tmp], [
|
||||
'input' => \is_string($validate) ? "required|{$validate}" :
|
||||
\array_merge(['required'], $validate)
|
||||
]);
|
||||
$validator->validate();
|
||||
/** @var string $value */
|
||||
$value = $tmp;
|
||||
} catch (ValidationException $e) {
|
||||
$this->error(sprintf(
|
||||
"{$errorMessage} %s",
|
||||
\implode(',', collect($e->errors())->flatten()->all())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
}; //end while
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random key for the application.
|
||||
*
|
||||
* ! Copy from \Illuminate\Foundation\Console\KeyGenerateCommand
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateRandomKey()
|
||||
{
|
||||
return 'base64:' . base64_encode(
|
||||
Encrypter::generateKey(config('app.cipher'))
|
||||
);
|
||||
}
|
||||
}
|
||||
15
app/Enums/EnvironmentScope.php
Normal file
15
app/Enums/EnvironmentScope.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum EnvironmentScope: string
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case all = '*';
|
||||
case staging = 'staging';
|
||||
case production = 'production';
|
||||
case reviewAny = 'review/*';
|
||||
}
|
||||
209
app/Lib/CdEnvVariable.php
Normal file
209
app/Lib/CdEnvVariable.php
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib;
|
||||
|
||||
use App\Enums\EnvironmentScope;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property-read \App\Enums\EnvironmentScope $environment_scope The scope on which the variable applies.
|
||||
* @property-read string $key The variable name.
|
||||
* @property-read string $value The variable value.
|
||||
* @property-read boolean $protected Export variable to pipelines running on
|
||||
* protected branches and tags only.
|
||||
* @property-read boolean $masked Variable will be masked in job logs.
|
||||
* Requires values to meet regular expression
|
||||
* requirements.
|
||||
* @property-read boolean $raw Char '$' will be treated as the start of a reference
|
||||
* to another variable.
|
||||
* @property-read boolean $isDefault Variable is needed for all projects.
|
||||
* @package App\Lib
|
||||
*/
|
||||
class CdEnvVariable implements \Stringable
|
||||
{
|
||||
/**
|
||||
* The k8s prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static string $k8sPrefix = 'APP';
|
||||
|
||||
/**
|
||||
* The prefix to use for defaults en vars
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static string $prefix = 'LRVL';
|
||||
|
||||
/**
|
||||
* The variable is default
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private bool $isDefault = false;
|
||||
|
||||
/**
|
||||
* Properties that can be accessed from the object.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private static array $readableProps = [
|
||||
'environment_scope', 'key', 'value', 'protected',
|
||||
'masked', 'raw', 'isDefault',
|
||||
];
|
||||
|
||||
/**
|
||||
* CdEnvVariable
|
||||
*
|
||||
* @param \App\Enums\EnvironmentScope $environment_scope The scope on which the variable applies.
|
||||
* @param string $key The variable name.
|
||||
* @param string $value The variable value.
|
||||
* @param boolean $protected Export variable to pipelines running on
|
||||
* protected branches and tags only.
|
||||
* @param boolean $masked Variable will be masked in job logs.
|
||||
* Requires values to meet regular expression requirements.
|
||||
* @param boolean $raw Char '$' will be treated as the start of a reference
|
||||
* to another variable.
|
||||
* @param string $variable_type Should always be 'env_var' for now.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected EnvironmentScope $environment_scope,
|
||||
protected string $key,
|
||||
protected string $value,
|
||||
protected bool $protected = false,
|
||||
protected bool $masked = false,
|
||||
protected bool $raw = false,
|
||||
protected string $variable_type = 'env_var',
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a CdEnvVariable
|
||||
*
|
||||
* @param array<string|boolean|\App\Enums\EnvironmentScope> $params
|
||||
* @return self
|
||||
*/
|
||||
public static function make(
|
||||
array $params
|
||||
): self {
|
||||
self::removeVarNamePrefixes($params);
|
||||
return new self(...$params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Property accessor
|
||||
*
|
||||
* @param string $name
|
||||
* @return boolean|string|\App\Enums\EnvironmentScope
|
||||
* @throws \Error If the property is not accessible.
|
||||
*/
|
||||
public function __get(string $name): bool|string|EnvironmentScope
|
||||
{
|
||||
if (\in_array($name, self::$readableProps)) {
|
||||
return $this->{$name};
|
||||
}
|
||||
throw new \Error('No property ' . __CLASS__ . "::{$name}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return is default.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function isProjectDefault(): self
|
||||
{
|
||||
$this->isDefault = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize var name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormatedGitlabKey(): string
|
||||
{
|
||||
// * Format with wanted prefixes
|
||||
if ($this->isDefault) {
|
||||
return sprintf(
|
||||
'%s_%s_%s',
|
||||
self::$k8sPrefix,
|
||||
self::$prefix,
|
||||
Str::upper($this->key)
|
||||
);
|
||||
}
|
||||
return sprintf(
|
||||
'%s_%s',
|
||||
self::$k8sPrefix,
|
||||
str::upper($this->key)
|
||||
);
|
||||
}
|
||||
|
||||
// * PRIVATE
|
||||
/**
|
||||
* Remove var name prefixes
|
||||
*
|
||||
* @param array<string|boolean|\App\Enums\EnvironmentScope> $params
|
||||
* @return void
|
||||
*/
|
||||
private static function removeVarNamePrefixes(array &$params): void
|
||||
{
|
||||
// * Force remove k8sPrefix
|
||||
if (Str::startsWith(Arr::get($params, 'key'), self::$k8sPrefix)) {
|
||||
Arr::set($params, 'key', Str::replaceStart(self::$k8sPrefix . '_', '', Arr::get($params, 'key')));
|
||||
}
|
||||
// * Force remove prefix
|
||||
if (Str::startsWith(Arr::get($params, 'key'), self::$prefix)) {
|
||||
Arr::set($params, 'key', Str::replaceStart(self::$prefix . '_', '', Arr::get($params, 'key')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of the env variable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$rowLength = 40;
|
||||
$titlePad = 20;
|
||||
$printStr = Str::of('')->newLine();
|
||||
|
||||
|
||||
// * TOP ROW
|
||||
$printStr = $printStr->append(\str_pad(" VAR `{$this->key}` ", $rowLength, '_', \STR_PAD_BOTH))->newLine();
|
||||
|
||||
$printStr = $printStr->append(\str_pad('value', $titlePad, pad_type: \STR_PAD_LEFT))
|
||||
->append(' => ');
|
||||
$value = $this->masked ?
|
||||
Str::mask($this->value, '*', 0, intval(ceil(strlen($this->value) * 0.65))) : $this->value;
|
||||
$printStr = $printStr->append("`{$value}`")->newLine();
|
||||
|
||||
$printStr = $printStr->append(\str_pad('environment_scope', $titlePad, pad_type: \STR_PAD_LEFT))
|
||||
->append(' => ')->append('`')
|
||||
->append($this->environment_scope->label())
|
||||
->append('`')->newLine();
|
||||
|
||||
if ($this->protected or $this->masked or $this->raw) {
|
||||
$printStr = $printStr->append(\str_pad('options', $titlePad, pad_type: \STR_PAD_LEFT))
|
||||
->append(' => ')
|
||||
->append(collect([
|
||||
$this->protected ? 'protected' : null,
|
||||
$this->masked ? 'masked' : null,
|
||||
$this->raw ? 'raw' : null,
|
||||
])->filter()->implode(','))->newLine();
|
||||
}
|
||||
$printStr = $printStr->append(\str_pad('variable_type', $titlePad, pad_type: \STR_PAD_LEFT))
|
||||
->append(' => ')
|
||||
->append("`{$this->variable_type}`");
|
||||
|
||||
// * BOTTOM ROW
|
||||
$printStr = $printStr->newLine()
|
||||
->append(\str_pad('', $rowLength, '_', \STR_PAD_LEFT))
|
||||
->newLine();
|
||||
|
||||
return $printStr->toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use GrahamCampbell\GitLab\Facades\GitLab;
|
||||
use GrahamCampbell\GitLab\GitLabManager;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
|
|
@ -23,5 +24,10 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
app()->bind(GitLabManager::class, function () {
|
||||
/** @var \Gitlab\Client */
|
||||
$client = GitLab::connection(GitLab::getDefaultConnection());
|
||||
return $client;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
263
app/Traits/Enums/BaseEnum.php
Normal file
263
app/Traits/Enums/BaseEnum.php
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Enums;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait BaseEnum
|
||||
{
|
||||
// * GENERIC METHODS
|
||||
|
||||
/**
|
||||
* Get enum value.
|
||||
*
|
||||
* @return string|integer
|
||||
*/
|
||||
public function value(): string|int
|
||||
{
|
||||
return self::toValues()[$this->name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum label.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string
|
||||
{
|
||||
return self::toLabels()[$this->name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
// * STATIC FETCH METHODS
|
||||
|
||||
/**
|
||||
* Get enum labels.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function toLabels(): array
|
||||
{
|
||||
if (empty(self::getCache('labels'))) {
|
||||
$labels = self::getLabelsConstant();
|
||||
self::setCache('labels', \collect(self::cases())
|
||||
->mapWithKeys(function ($enum) use ($labels) {
|
||||
if (!\is_null($labels) and !isset($labels[$enum->name])) {
|
||||
throw new \Error('Missing definition label ' . self::class . " for {$enum->name}");
|
||||
}
|
||||
return [$enum->name => \trans(
|
||||
!\is_null($labels) ? $labels[$enum->name] : Str::ucfirst($enum->name)
|
||||
)];
|
||||
})->all());
|
||||
}
|
||||
return self::getCache('labels');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum values.
|
||||
*
|
||||
* @return array<integer|string>
|
||||
*/
|
||||
public static function toValues(): array
|
||||
{
|
||||
if (empty(self::getCache('values'))) {
|
||||
self::setCache('values', \collect(self::cases())
|
||||
->mapWithKeys(fn ($enum) => [$enum->name => $enum->value])->all());
|
||||
}
|
||||
return self::getCache('values');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enums.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function all(): array
|
||||
{
|
||||
return self::cases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enums.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function toArray(): array
|
||||
{
|
||||
if (empty(self::getCache('enums'))) {
|
||||
$definitions = self::getCustomDefinitions();
|
||||
self::setCache('enums', \collect(self::cases())
|
||||
->mapWithKeys(function ($enum) use ($definitions) {
|
||||
$baseArray = [
|
||||
'name' => $enum->name,
|
||||
'label' => $enum->label(),
|
||||
'value' => $enum->value,
|
||||
];
|
||||
foreach ($definitions as $index => $elements) {
|
||||
$index = Str::lower(Str::singular($index));
|
||||
if (!isset($elements[$enum->name])) {
|
||||
throw new \Error("Missing definition {$index} " . self::class . " for {$enum->name}");
|
||||
}
|
||||
$baseArray[$index] = $elements[$enum->name];
|
||||
}
|
||||
return [$enum->name => (object)$baseArray];
|
||||
})->all());
|
||||
}
|
||||
return self::getCache('enums');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch enum using value.
|
||||
*
|
||||
* @param self|integer|string $value
|
||||
* @param string $field
|
||||
* @return self|null
|
||||
*/
|
||||
public static function make(self|int|string $value, string $field = 'value'): self|null
|
||||
{
|
||||
if ($field === "label") {
|
||||
$enumLabels = array_change_key_case(array_flip(self::toLabels()), CASE_LOWER);
|
||||
$value = (isset($enumLabels[strtolower($value)])) ? $enumLabels[strtolower($value)] : false;
|
||||
$field = 'name';
|
||||
}
|
||||
return !\is_object($value) ?
|
||||
\collect(self::cases())->keyBy($field)->get($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if 2 enum are equals.
|
||||
*
|
||||
* @param object ...$others
|
||||
* @return boolean
|
||||
* @throws \Error If provided element are not enums that extends BaseEnum.
|
||||
*/
|
||||
public function equals(object ...$others): bool
|
||||
{
|
||||
foreach ($others as $other) {
|
||||
if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) {
|
||||
throw new \Error(__METHOD__ .
|
||||
' requires $enum to use ' . BaseEnum::class);
|
||||
}
|
||||
if (
|
||||
get_class($this) === get_class($other)
|
||||
&& $this->value === $other->value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize to json.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
|
||||
// * CACHE
|
||||
|
||||
/**
|
||||
* Get cache array.
|
||||
*
|
||||
* @param string $index
|
||||
* @return array
|
||||
*/
|
||||
private static function getCache(string $index): array
|
||||
{
|
||||
self::initCache();
|
||||
if (!isset($GLOBALS[self::class]['props'][$index])) {
|
||||
$GLOBALS[self::class]['props'][$index] = [];
|
||||
}
|
||||
return $GLOBALS[self::class]['props'][$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache array.
|
||||
*
|
||||
* @param string $index
|
||||
* @param array $items
|
||||
* @return void
|
||||
*/
|
||||
private static function setCache(string $index, array $items): void
|
||||
{
|
||||
self::initCache();
|
||||
if (!isset($GLOBALS[self::class]['props'][$index])) {
|
||||
$GLOBALS[self::class]['props'][$index] = [];
|
||||
}
|
||||
$GLOBALS[self::class]['props'][$index] = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init cache if needed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function initCache(): void
|
||||
{
|
||||
if (!isset($GLOBALS[self::class])) {
|
||||
$GLOBALS[self::class] = [
|
||||
'props' => [
|
||||
'enums' => [],
|
||||
'values' => [],
|
||||
'labels' => []
|
||||
],
|
||||
'customProps' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// * PRIVATE
|
||||
|
||||
/**
|
||||
* Fetch the LABELS constant if exists.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private static function getLabelsConstant(): array|null
|
||||
{
|
||||
$class_name = self::class;
|
||||
try {
|
||||
$constant_reflex = new \ReflectionClassConstant($class_name, 'LABELS');
|
||||
return $constant_reflex->getValue();
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom constants.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getCustomDefinitions(): array
|
||||
{
|
||||
// ? Has custom props in cache ?
|
||||
if (!isset($GLOBALS[self::class]) or \is_null($GLOBALS[self::class]['customProps'])) {
|
||||
$names = \collect(self::cases())->keyBy('name');
|
||||
|
||||
$GLOBALS[self::class]['customProps'] = \collect((new \ReflectionClass(self::class))->getConstants())
|
||||
->filter(function (mixed $constant, string $key) use ($names) {
|
||||
if ($key === 'LABELS' or !\is_array($constant)) {
|
||||
return false;
|
||||
}
|
||||
return \collect($constant)->keys()
|
||||
->filter(fn (string $name) => $names->has($name))->count();
|
||||
})->all();
|
||||
}
|
||||
return $GLOBALS[self::class]['customProps'];
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@
|
|||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^10.10",
|
||||
"laravel/sanctum": "^3.3",
|
||||
"laravel/tinker": "^2.8"
|
||||
"laravel/tinker": "^2.8",
|
||||
"sebastian/diff": "^5.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
|
|
|||
136
composer.lock
generated
136
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "8cfd935253b074b4600945f52beceb9a",
|
||||
"content-hash": "14267b564f41df466ad8f3fffd3b577b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
|
@ -4101,6 +4101,73 @@
|
|||
],
|
||||
"time": "2023-11-08T05:53:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "5.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"symfony/process": "^4.2 || ^5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "5.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de"
|
||||
},
|
||||
{
|
||||
"name": "Kore Nordmann",
|
||||
"email": "mail@kore-nordmann.de"
|
||||
}
|
||||
],
|
||||
"description": "Diff implementation",
|
||||
"homepage": "https://github.com/sebastianbergmann/diff",
|
||||
"keywords": [
|
||||
"diff",
|
||||
"udiff",
|
||||
"unidiff",
|
||||
"unified diff"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-22T10:55:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"version": "v6.4.2",
|
||||
|
|
@ -8707,73 +8774,6 @@
|
|||
],
|
||||
"time": "2023-12-21T08:37:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
"version": "5.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/diff.git",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"symfony/process": "^4.2 || ^5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "5.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Bergmann",
|
||||
"email": "sebastian@phpunit.de"
|
||||
},
|
||||
{
|
||||
"name": "Kore Nordmann",
|
||||
"email": "mail@kore-nordmann.de"
|
||||
}
|
||||
],
|
||||
"description": "Diff implementation",
|
||||
"homepage": "https://github.com/sebastianbergmann/diff",
|
||||
"keywords": [
|
||||
"diff",
|
||||
"udiff",
|
||||
"unidiff",
|
||||
"unified diff"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/diff/issues",
|
||||
"security": "https://github.com/sebastianbergmann/diff/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/diff/tree/5.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-22T10:55:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "6.0.1",
|
||||
|
|
|
|||
|
|
@ -41,15 +41,10 @@ return [
|
|||
'connections' => [
|
||||
|
||||
'main' => [
|
||||
'token' => 'your-token',
|
||||
'token' => env('GITLAB_ADMIN_TOKEN'),
|
||||
'url' => env('GITLAB_URL'),
|
||||
'method' => 'token'
|
||||
],
|
||||
|
||||
'alternative' => [
|
||||
'token' => 'your-token',
|
||||
'method' => 'oauth'
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
@ -70,11 +65,6 @@ return [
|
|||
'connector' => null
|
||||
],
|
||||
|
||||
'bar' => [
|
||||
'driver' => 'illuminate',
|
||||
'connector' => 'redis'
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue