1
0
Fork 0
mirror of synced 2024-10-01 17:58:02 +13:00

Update naming convention, add new VCS endpoints, support silent mode and rootDirectory

This commit is contained in:
Matej Bačo 2023-06-07 17:50:32 +02:00
parent e4c79d5d09
commit 5e221e285f
18 changed files with 423 additions and 630 deletions

View file

@ -2221,10 +2221,10 @@ $collections = [
],
],
'vcs_installations' => [
'vcsInstallations' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('vcs_installations'),
'name' => 'vcs_installations',
'$id' => ID::custom('vcsInstallations'),
'name' => 'vcsInstallations',
'attributes' => [
[
'$id' => ID::custom('projectId'),
@ -2296,10 +2296,10 @@ $collections = [
'indexes' => [],
],
'vcs_repos' => [
'vcsRepos' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('vcs_repos'),
'name' => 'vcs_repos',
'$id' => ID::custom('vcsRepos'),
'name' => 'vcsRepos',
'attributes' => [
[
'$id' => ID::custom('vcsInstallationId'),
@ -2440,16 +2440,6 @@ $collections = [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('repositoryId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRepositoryId'),
'type' => Database::VAR_STRING,
@ -2461,7 +2451,17 @@ $collections = [
'array' => false,
],
[
'$id' => ID::custom('vcsRepositoryInternalId'),
'$id' => ID::custom('vcsRepositoryDocId'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRepositoryDocInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@ -2472,7 +2472,7 @@ $collections = [
'filters' => [],
],
[
'$id' => ID::custom('branch'),
'$id' => ID::custom('vcsBranch'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
@ -2481,6 +2481,27 @@ $collections = [
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRootDirectory'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsSilentMode'),
'type' => Database::VAR_BOOLEAN,
'signed' => true,
'size' => 0,
'format' => '',
'filters' => [],
'required' => false,
'default' => false,
'array' => false,
],
[
'$id' => ID::custom('logging'),
'type' => Database::VAR_BOOLEAN,
@ -2676,9 +2697,9 @@ $collections = [
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_vcsRepositoryId'),
'$id' => ID::custom('_key_vcsRepositoryDocId'),
'type' => Database::INDEX_KEY,
'attributes' => ['vcsRepositoryId'],
'attributes' => ['vcsRepositoryDocId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
@ -2890,7 +2911,17 @@ $collections = [
'array' => false,
],
[
'$id' => ID::custom('branch'),
'$id' => ID::custom('vcsBranch'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,
'format' => '',
'filters' => [],
'required' => false,
'array' => false,
],
[
'$id' => ID::custom('vcsRootDirectory'),
'type' => Database::VAR_STRING,
'signed' => true,
'size' => Database::LENGTH_KEY,

View file

@ -330,6 +330,11 @@ return [
'description' => 'Installation with the requested ID could not be found.',
'code' => 404,
],
Exception::REPOSITORY_NOT_FOUND => [
'name' => Exception::REPOSITORY_NOT_FOUND,
'description' => 'Repository with the requested ID could not be found.',
'code' => 404,
],
/** Functions */
Exception::FUNCTION_NOT_FOUND => [

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 2f4e16038db4968371ef3a5b1189294d3dc60788
Subproject commit 42938250c6ee923d88d6f5b3c04484712a5a176f

View file

@ -523,16 +523,18 @@ App::put('/v1/functions/:functionId')
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.', true)
->param('buildCommand', '', new Text('1028'), 'Build Command.', true)
->param('installCommand', '', new Text('1028'), 'Install Command.', true)
->param('installationId', '', new Text(128), 'Appwrite Installation ID for vcs deployment.', true)
->param('repositoryId', '', new Text(128), 'Repository ID of the repo linked to the function', true)
->param('branch', 'main', new Text(128), 'Production branch for the repo linked to the function', true)
->param('vcsInstallationId', '', new Text(128), 'Appwrite Installation ID for vcs deployment.', true)
->param('vcsRepositoryId', '', new Text(128), 'Repository ID of the repo linked to the function', true)
->param('vcsBranch', '', new Text(128), 'Production branch for the repo linked to the function', true)
->param('vcsSilentMode', false, new Boolean(), 'Is VCS connection is in silent mode for the repo linked to the function?', true)
->param('vcsRootDirectory', '', new Text(128), 'Path to function code in the linked repo', true)
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->inject('events')
->inject('dbForConsole')
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $buildCommand, string $installCommand, string $vcsInstallationId, string $repositoryId, string $branch, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $buildCommand, string $installCommand, string $vcsInstallationId, string $vcsRepositoryId, string $vcsBranch, bool $vcsSilentMode, string $vcsRootDirectory, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@ -541,7 +543,7 @@ App::put('/v1/functions/:functionId')
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
@ -549,12 +551,8 @@ App::put('/v1/functions/:functionId')
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
if (!empty($vcsInstallationId) && empty($repositoryId)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID); // TODO: More specific error
}
if (!empty($repositoryId) && empty($vcsInstallationId)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID); // TODO: More specific error
if (!empty($vcsRepositoryId) && (empty($vcsInstallationId) || empty($vcsBranch))) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'When connecting to VCS you need to provide all VCS parameters.');
}
if ($function->isEmpty()) {
@ -563,22 +561,27 @@ App::put('/v1/functions/:functionId')
$enabled ??= $function->getAttribute('enabled', true);
$vcsRepoId = null;
$needToDeploy = false;
//if repo id was previously empty and non empty now, we need to create a new deployment for this function
$prevVcsRepoId = $function->getAttribute('vcsRepositoryId', '');
if (empty($prevVcsRepoId) && !empty($repositoryId)) {
$needToDeploy = true;
$vcsRepositoryDocId = $function->getAttribute('vcsRepositoryDocId', '');
$vcsRepositoryDocInternalId = $function->getAttribute('vcsRepositoryDocInternalId', '');
$isConnected = !empty($function->getAttribute('vcsRepositoryId', ''));
// Git disconnect logic
if ($isConnected && empty($vcsRepositoryId)) {
$dbForConsole->deleteDocument('vcsRepos', $function->getAttribute('vcsRepositoryId', ''));
$vcsRepositoryId = '';
$vcsInstallationId = '';
$vcsBranch = '';
$vcsRootDirectory = '';
$vcsSilentMode = true;
$vcsRepositoryDocId = '';
$vcsRepositoryDocInternalId = '';
}
// activate the deployment for first run of a VCS repo
if ($needToDeploy) {
$deploymentId = ID::unique();
$entrypoint = $function->getAttribute('entrypoint', '');
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
//Add document in VCS repos collection
$vcs_repos = new Document([
// Git connect logic
if (!$isConnected && !empty($vcsRepositoryId)) {
$vcsRepoDoc = $dbForConsole->createDocument('vcsRepos', new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::any()),
@ -589,15 +592,18 @@ App::put('/v1/functions/:functionId')
'vcsInstallationInternalId' => $installation->getInternalId(),
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'repositoryId' => $repositoryId,
'repositoryId' => $vcsRepositoryId,
'resourceId' => $functionId,
'resourceType' => "function"
]);
'resourceType' => 'function'
]));
$vcs_repos = $dbForConsole->createDocument('vcs_repos', $vcs_repos);
$vcsRepoId = $vcs_repos->getId();
$vcsRepoInternalId = $vcs_repos->getInternalId();
$vcsRepositoryDocId = $vcsRepoDoc->getId();
$vcsRepositoryDocInternalId = $vcsRepoDoc->getInternalId();
}
// Redeploy logic
if (!$isConnected && !empty($vcsRepositoryId)) {
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
@ -613,19 +619,21 @@ App::put('/v1/functions/:functionId')
'type' => 'vcs',
'vcsInstallationId' => $installation->getId(),
'vcsInstallationInternalId' => $installation->getInternalId(),
'vcsRepositoryId' => $vcsRepoId,
'vcsRepositoryInternalId' => $vcsRepoInternalId,
'branch' => $branch,
'vcsRepositoryId' => $vcsRepositoryDocId,
'vcsRepositoryInternalId' => $vcsRepositoryDocInternalId,
'vcsBranch' => $vcsBranch,
'vcsRootDirectory' => $vcsRootDirectory,
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => true,
]));
}
// Disconnect repo
if (!empty($prevVcsRepoId) && empty($repositoryId)) {
$dbForConsole->deleteDocument('vcs_repos', $prevVcsRepoId);
$vcsRepoId = '';
$vcsRepoInternalId = '';
$buildEvent = new Build();
$buildEvent
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setProject($project)
->trigger();
}
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
@ -641,10 +649,12 @@ App::put('/v1/functions/:functionId')
'installCommand' => $installCommand,
'vcsInstallationId' => $installation->getId(),
'vcsInstallationInternalId' => $installation->getInternalId(),
'repositoryId' => $repositoryId,
'vcsRepositoryId' => $vcsRepoId ?? $function->getAttribute('vcsRepositoryId', ''),
'vcsRepositoryInternalId' => $vcsRepoInternalId ?? $function->getAttribute('vcsRepositoryInternalId', ''),
'branch' => $branch,
'vcsRepositoryId' => $vcsRepositoryId,
'vcsRepositoryDocId' => $vcsRepositoryDocId,
'vcsRepositoryDocInternalId' => $vcsRepositoryDocInternalId,
'vcsBranch' => $vcsBranch,
'vcsRootDirectory' => $vcsRootDirectory,
'vcsSilentMode' => $vcsSilentMode,
'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]),
])));
@ -659,18 +669,6 @@ App::put('/v1/functions/:functionId')
$eventsInstance->setParam('functionId', $function->getId());
if ($needToDeploy) {
$buildEvent = new Build();
$buildEvent
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setProject($project)
->trigger();
//TODO: Add event?
}
$response->dynamic($function, Response::MODEL_FUNCTION);
});

View file

@ -89,7 +89,7 @@ App::get('/v1/vcs/github/incominginstallation')
$projectInternalId = $project->getInternalId();
$vcsInstallation = $dbForConsole->findOne('vcs_installations', [
$vcsInstallation = $dbForConsole->findOne('vcsInstallations', [
Query::equal('installationId', [$installationId]),
Query::equal('projectInternalId', [$projectInternalId])
]);
@ -110,10 +110,10 @@ App::get('/v1/vcs/github/incominginstallation')
'accessToken' => null
]);
$vcsInstallation = $dbForConsole->createDocument('vcs_installations', $vcsInstallation);
$vcsInstallation = $dbForConsole->createDocument('vcsInstallations', $vcsInstallation);
} else {
$vcsInstallation = $vcsInstallation->setAttribute('organization', $owner);
$vcsInstallation = $dbForConsole->updateDocument('vcs_installations', $vcsInstallation->getId(), $vcsInstallation);
$vcsInstallation = $dbForConsole->updateDocument('vcsInstallations', $vcsInstallation->getId(), $vcsInstallation);
}
$response
@ -122,7 +122,7 @@ App::get('/v1/vcs/github/incominginstallation')
->redirect($redirect);
});
App::get('v1/vcs/github/installations/:installationId/repositories')
App::get('/v1/vcs/github/installations/:installationId/repositories')
->desc('List repositories')
->groups(['api', 'vcs'])
->label('scope', 'public')
@ -143,7 +143,7 @@ App::get('v1/vcs/github/installations/:installationId/repositories')
$search = "";
}
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
@ -193,7 +193,49 @@ App::get('v1/vcs/github/installations/:installationId/repositories')
]), Response::MODEL_REPOSITORY_LIST);
});
App::get('v1/vcs/github/installations/:installationId/repositories/:repositoryId/branches')
App::get('/v1/vcs/github/installations/:installationId/repositories/:repositoryId')
->desc('Get repository')
->groups(['api', 'vcs'])
->label('scope', 'public')
->label('sdk.namespace', 'vcs')
->label('sdk.method', 'getRepository')
->label('sdk.description', '')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_REPOSITORY)
->param('installationId', '', new Text(256), 'Installation Id')
->param('repositoryId', '', new Text(256), 'Repository Id')
->inject('gitHub')
->inject('response')
->inject('project')
->inject('dbForConsole')
->action(function (string $vcsInstallationId, string $repositoryId, GitHub $github, Response $response, Document $project, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$installationId = $installation->getAttribute('installationId');
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$repositoryName = $github->getRepositoryName($repositoryId);
if (empty($repositoryName)) {
throw new Exception(Exception::REPOSITORY_NOT_FOUND);
}
$repository = $github->getRepository($owner, $repositoryName);
$response->dynamic(new Document($repository), Response::MODEL_REPOSITORY);
});
App::get('/v1/vcs/github/installations/:installationId/repositories/:repositoryId/branches')
->desc('List Repository Branches')
->groups(['api', 'vcs'])
->label('scope', 'public')
@ -210,7 +252,7 @@ App::get('v1/vcs/github/installations/:installationId/repositories/:repositoryId
->inject('project')
->inject('dbForConsole')
->action(function (string $vcsInstallationId, string $repositoryId, GitHub $github, Response $response, Document $project, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
@ -308,7 +350,7 @@ $createGitDeployments = function (GitHub $github, string $installationId, array
// TODO: Figure out port
$targetUrl = $request->getProtocol() . '://' . $request->getHostname() . ":3000/console/project-$projectId/functions/function-$functionId";
if (!empty($SHA)) {
if (!empty($SHA) && $function->getAttribute('vcsSilentMode', false) === false) {
$functionName = $function->getAttribute('name');
$projectName = $project->getAttribute('name');
$name = "{$functionName} ({$projectName})";
@ -320,7 +362,6 @@ $createGitDeployments = function (GitHub $github, string $installationId, array
$github->updateCommitStatus($repositoryName, $SHA, $owner, 'pending', $message, $targetUrl, $name);
}
$buildEvent = new Build();
$buildEvent
->setType(BUILD_TYPE_DEPLOYMENT)
@ -363,7 +404,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
//find functionId from functions table
$vcsRepos = $dbForConsole->find('vcs_repos', [
$vcsRepos = $dbForConsole->find('vcsRepos', [
Query::equal('repositoryId', [$repositoryId]),
Query::limit(100),
]);
@ -374,22 +415,22 @@ App::post('/v1/vcs/github/incomingwebhook')
// TODO: Use worker for this job instead (update function as well)
$installationId = $parsedPayload["installationId"];
$vcsInstallations = $dbForConsole->find('vcs_installations', [
$vcsInstallations = $dbForConsole->find('vcsInstallations', [
Query::equal('installationId', [$installationId]),
Query::limit(1000)
]);
foreach ($vcsInstallations as $installation) {
$vcsRepos = $dbForConsole->find('vcs_repos', [
$vcsRepos = $dbForConsole->find('vcsRepos', [
Query::equal('vcsInstallationId', [$installation->getId()]),
Query::limit(1000)
]);
foreach ($vcsRepos as $repo) {
$dbForConsole->deleteDocument('vcs_repos', $repo->getId());
$dbForConsole->deleteDocument('vcsRepos', $repo->getId());
}
$dbForConsole->deleteDocument('vcs_installations', $installation->getId());
$dbForConsole->deleteDocument('vcsInstallations', $installation->getId());
}
}
} elseif ($event == $github::EVENT_PULL_REQUEST) {
@ -403,7 +444,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$vcsRepos = $dbForConsole->find('vcs_repos', [
$vcsRepos = $dbForConsole->find('vcsRepos', [
Query::equal('repositoryId', [$repositoryId]),
Query::orderDesc('$createdAt')
]);
@ -447,7 +488,7 @@ App::post('/v1/vcs/github/incomingwebhook')
}
$commentId = '';
if (!$comment->isEmpty()) {
if (!$comment->isEmpty() && $function->getAttribute('vcsSilentMode', false) === false) {
$commentId = $github->createComment($owner, $repositoryName, $pullRequestNumber, $comment->generateComment());
}
@ -493,7 +534,7 @@ App::get('/v1/vcs/installations')
if ($cursor) {
/** @var Query $cursor */
$vcsInstallationId = $cursor->getValue();
$cursorDocument = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId);
$cursorDocument = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Installation '{$vcsInstallationId}' for the 'cursor' value not found.");
@ -504,8 +545,8 @@ App::get('/v1/vcs/installations')
$filterQueries = Query::groupByType($queries)['filters'];
$results = $dbForConsole->find('vcs_installations', $queries);
$total = $dbForConsole->count('vcs_installations', $filterQueries, APP_LIMIT_COUNT);
$results = $dbForConsole->find('vcsInstallations', $queries);
$total = $dbForConsole->count('vcsInstallations', $filterQueries, APP_LIMIT_COUNT);
if (\count($results) > 0) {
$installationIds = \array_map(fn ($result) => $result->getId(), $results);
@ -528,6 +569,43 @@ App::get('/v1/vcs/installations')
]), Response::MODEL_INSTALLATION_LIST);
});
App::get('/v1/vcs/installations/:installationId')
->groups(['api', 'vcs'])
->desc('Get installations')
->label('scope', 'public')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'vcs')
->label('sdk.method', 'getInstallation')
->label('sdk.description', '/docs/references/vcs/get-installation.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_INSTALLATION)
->param('installationId', '', new Text(256), 'Installation Id')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('dbForConsole')
->action(function (string $installationId, Response $response, Document $project, Database $dbForProject, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('vcsInstallations', $installationId);
if ($installation === false || $installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
if ($installation->getAttribute('projectInternalId') !== $project->getInternalId()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$functions = Authorization::skip(fn () => $dbForProject->find('functions', [
Query::equal('vcsInstallationId', [$installation->getId()]),
Query::limit(APP_LIMIT_SUBQUERY)
]));
$installation->setAttribute('functions', $functions);
$response->dynamic($installation, Response::MODEL_INSTALLATION);
});
App::delete('/v1/vcs/installations/:installationId')
->groups(['api', 'vcs'])
->desc('Delete Installation')
@ -545,7 +623,7 @@ App::delete('/v1/vcs/installations/:installationId')
->inject('deletes')
->action(function (string $vcsInstallationId, Response $response, Document $project, Database $dbForConsole, Delete $deletes) {
$installation = $dbForConsole->getDocument('vcs_installations', $vcsInstallationId, [
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
Query::equal('projectInternalId', [$project->getInternalId()])
]);
@ -553,7 +631,7 @@ App::delete('/v1/vcs/installations/:installationId')
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
if (!$dbForConsole->deleteDocument('vcs_installations', $installation->getId())) {
if (!$dbForConsole->deleteDocument('vcsInstallations', $installation->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove installation from DB');
}

View file

@ -154,7 +154,7 @@ const DELETE_TYPE_ABUSE = 'abuse';
const DELETE_TYPE_USAGE = 'usage';
const DELETE_TYPE_REALTIME = 'realtime';
const DELETE_TYPE_BUCKETS = 'buckets';
const DELETE_TYPE_INSTALLATIONS = 'vcs_installations';
const DELETE_TYPE_INSTALLATIONS = 'vcsInstallations';
const DELETE_TYPE_RULES = 'rules';
const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';

View file

@ -108,25 +108,30 @@ class BuildsV1 extends Worker
$isVcsEnabled = $vcsRepoId ? true : false;
if ($isVcsEnabled) {
$vcsRepos = Authorization::skip(fn () => $dbForConsole
->getDocument('vcs_repos', $vcsRepoId));
$vcsRepos = Authorization::skip(fn () => $dbForConsole->getDocument('vcsRepos', $vcsRepoId));
$repositoryId = $vcsRepos->getAttribute('repositoryId');
$vcsInstallations = Authorization::skip(fn () => $dbForConsole->getDocument('vcs_installations', $vcsInstallationId));
$vcsInstallations = Authorization::skip(fn () => $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId));
$installationId = $vcsInstallations->getAttribute('installationId');
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$tmpDirectory = '/tmp/builds/' . $buildId . '/code';
$rootDirectory = $function->getAttribute('vcsRootDirectory', '');
$rootDirectory = \rtrim($rootDirectory, '/');
$rootDirectory = \ltrim($rootDirectory, '.');
$rootDirectory = \ltrim($rootDirectory, '/');
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($installationId);
$repositoryName = $github->getRepositoryName($repositoryId);
$branchName = $deployment->getAttribute('branch');
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryId, $branchName);
$branchName = $deployment->getAttribute('vcsBranch');
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryId, $branchName, $tmpDirectory, $rootDirectory);
$stdout = '';
$stderr = '';
Console::execute('mkdir /tmp/builds/' . $buildId, '', $stdout, $stderr);
Console::execute($gitCloneCommand . ' /tmp/builds/' . $buildId . '/code', '', $stdout, $stderr);
Console::execute('tar --exclude code.tar.gz -czf /tmp/builds/' . $buildId . '/code.tar.gz -C /tmp/builds/' . $buildId . '/code .', '', $stdout, $stderr);
Console::execute('mkdir -p /tmp/builds/' . $buildId, '', $stdout, $stderr);
Console::execute($gitCloneCommand, '', $stdout, $stderr);
Console::execute('tar --exclude code.tar.gz -czf /tmp/builds/' . $buildId . '/code.tar.gz -C /tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
$deviceFunctions = $this->getFunctionsDevice($project->getId());
@ -373,6 +378,10 @@ class BuildsV1 extends Worker
protected function runGitAction(string $status, GitHub $github, string $SHA, string $owner, string $repositoryName, string $targetUrl, Document $project, Document $function, string $deploymentId, Database $dbForProject)
{
if ($function->getAttribute('vcsSilentMode', false) === true) {
return;
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$commentId = $deployment->getAttribute('vcsCommentId', '');

View file

@ -703,43 +703,6 @@ class DeletesV1 extends Worker
Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
}
/**
* @param string $collection collectionID
* @param Query[] $queries
* @param Database $database
* @param callable $callback
*/
protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void
{
$count = 0;
$chunk = 0;
$limit = 50;
$results = [];
$sum = $limit;
$executionStart = \microtime(true);
while ($sum === $limit) {
$chunk++;
$results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
$sum = count($results);
foreach ($results as $document) {
if (is_callable($callback)) {
$callback($document);
}
$count++;
}
}
$executionEnd = \microtime(true);
Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds");
}
/**
* @param Document $document rule document
* @param Document $project project document
@ -780,13 +743,13 @@ class DeletesV1 extends Worker
protected function deleteInstallation(Document $document, Document $project)
{
$dbForProject = $this->getProjectDB($projectId);
$dbForProject = $this->getProjectDB($project);
$dbForConsole = $this->getConsoleDB();
$this->listByGroup('functions', [
Query::equal('vcsInstallationInternalId', [$document->getInternalId()])
], $dbForProject, function ($function) use ($dbForProject, $dbForConsole) {
$dbForConsole->deleteDocument('vcs_repos', $function->getAttribute('vcsRepositoryId'));
$dbForConsole->deleteDocument('vcsRepos', $function->getAttribute('vcsRepositoryId'));
$function = $function
->setAttribute('vcsInstallationId', '')

644
composer.lock generated
View file

@ -406,68 +406,6 @@
],
"time": "2022-01-18T15:43:28+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831",
"reference": "672eff8cf1d6fe1ef09ca0f89c4b287d6a3eb831",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.1"
},
"require-dev": {
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
},
"type": "library",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"time": "2023-02-25T20:23:15+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.7.0",
@ -1123,81 +1061,6 @@
],
"time": "2022-02-28T15:31:21+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.1",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e",
"reference": "dd3a383e599f49777d8b628dadbb90cae435b87e",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"time": "2023-02-25T19:38:58+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.2",
@ -1658,254 +1521,6 @@
],
"time": "2023-03-01T10:25:55+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "utopia-php/abuse",
"version": "0.16.0",
@ -3090,14 +2705,13 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "9215381b4fde95e26ab1f01d5add6d2d428d6fd7"
"reference": "927140be98f87659ff24145f4d37686a5e0f5fb3"
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
"utopia-php/cache": "^0.8.0",
"utopia-php/framework": "0.26.*",
"vlucas/phpdotenv": "^5.5"
"utopia-php/framework": "0.26.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
@ -3106,7 +2720,8 @@
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\VCS\\": "src/VCS"
"Utopia\\VCS\\": "src/VCS",
"Utopia\\Detector\\": "src/Detector"
}
},
"autoload-dev": {
@ -3135,7 +2750,7 @@
"utopia",
"vcs"
],
"time": "2023-05-28T10:14:37+00:00"
"time": "2023-06-07T14:30:22+00:00"
},
{
"name": "utopia-php/websocket",
@ -3195,90 +2810,6 @@
},
"time": "2021-12-20T10:50:09+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.5.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
"reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.0.2",
"php": "^7.1.3 || ^8.0",
"phpoption/phpoption": "^1.8",
"symfony/polyfill-ctype": "^1.23",
"symfony/polyfill-mbstring": "^1.23.1",
"symfony/polyfill-php80": "^1.23.1"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"ext-filter": "*",
"phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "5.5-dev"
}
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://github.com/vlucas"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"time": "2022-10-16T01:01:54+00:00"
},
{
"name": "webmozart/assert",
"version": "1.11.0",
@ -5619,6 +5150,171 @@
],
"time": "2022-04-18T20:38:04+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "textalk/websocket",
"version": "1.5.7",

View file

@ -108,6 +108,7 @@ class Exception extends \Exception
/** VCS */
public const INSTALLATION_NOT_FOUND = 'installation_not_found';
public const REPOSITORY_NOT_FOUND = 'repository_not_found';
/** Functions */
public const FUNCTION_NOT_FOUND = 'function_not_found';

View file

@ -15,6 +15,6 @@ class Installations extends Base
*/
public function __construct()
{
parent::__construct('vcs_installations', self::ALLOWED_ATTRIBUTES);
parent::__construct('vcsInstallations', self::ALLOWED_ATTRIBUTES);
}
}

View file

@ -117,18 +117,30 @@ class Func extends Model
'default' => '',
'example' => '644051bd6572792165cc',
])
->addRule('repositoryId', [
->addRule('vcsRepositoryId', [
'type' => self::TYPE_STRING,
'description' => 'Git Repository ID',
'default' => '',
'example' => 'appwrite',
])
->addRule('branch', [
->addRule('vcsBranch', [
'type' => self::TYPE_STRING,
'description' => 'Git branch name',
'default' => '',
'example' => 'main',
])
->addRule('vcsRootDirectory', [
'type' => self::TYPE_STRING,
'description' => 'Path to function in git repository',
'default' => '',
'example' => 'functions/helloWorld',
])
->addRule('vcsSilentMode', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is VCS connection is in silent mode?',
'default' => false,
'example' => false,
])
;
}