log streaming, locking GH comments, execution headers, locale header, redeploy logic
This commit is contained in:
parent
399847d7ea
commit
cc06a4bdaa
|
@ -2452,6 +2452,16 @@ $collections = [
|
|||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('live'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'signed' => true,
|
||||
'size' => 0,
|
||||
'format' => '',
|
||||
'filters' => [],
|
||||
'required' => true,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('vcsInstallationId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
|
@ -4320,6 +4330,14 @@ $collections = [
|
|||
],
|
||||
],
|
||||
],
|
||||
|
||||
'vcsCommentLocks' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('vcsCommentLocks'),
|
||||
'name' => 'vcsCommentLocks',
|
||||
'attributes' => [],
|
||||
'indexes' => []
|
||||
]
|
||||
];
|
||||
|
||||
return $collections;
|
||||
|
|
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
|
@ -44,10 +44,11 @@ use Utopia\CLI\Console;
|
|||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use MaxMind\Db\Reader;
|
||||
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
$redeployVcsLogic = function (Document $function, Document $project, Document $installation, Database $dbForProject, Document $template) {
|
||||
$redeployVcsLogic = function (Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Document $template) {
|
||||
$deploymentId = ID::unique();
|
||||
$entrypoint = $function->getAttribute('entrypoint', '');
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
|
@ -74,11 +75,17 @@ $redeployVcsLogic = function (Document $function, Document $project, Document $i
|
|||
'activate' => true,
|
||||
]));
|
||||
|
||||
$projectId = $project->getId();
|
||||
$functionId = $function->getId();
|
||||
|
||||
$targetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId";
|
||||
|
||||
$buildEvent = new Build();
|
||||
$buildEvent
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setTargetUrl($targetUrl)
|
||||
->setTemplate($template)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
@ -107,7 +114,7 @@ App::post('/v1/functions')
|
|||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
|
||||
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
|
||||
->param('logging', true, new Boolean(), 'Do executions get logged?', true)
|
||||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.', true)
|
||||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.')
|
||||
->param('buildCommand', '', new Text('1028'), 'Build Command.', true)
|
||||
->param('installCommand', '', new Text('1028'), 'Install Command.', true)
|
||||
->param('vcsInstallationId', '', new Text(128), 'Appwrite Installation ID for vcs deployment.', true)
|
||||
|
@ -117,22 +124,25 @@ App::post('/v1/functions')
|
|||
->param('vcsRootDirectory', '', new Text(128), 'Path to function code in the linked repo', true)
|
||||
->param('templateRepositoryName', '', new Text(128), 'Repository name of the template', true)
|
||||
->param('templateOwnerName', '', new Text(128), 'Owner name of the template', true)
|
||||
->param('templateDirectory', '', new Text(128), 'Path to function code in the template repo', true)
|
||||
->param('templateRootDirectory', '', new Text(128), 'Path to function code in the template repo', true)
|
||||
->param('templateBranch', '', new Text(128), 'Branch of template repo with the code', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $functionId, string $name, string $runtime, 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, string $templateRepositoryName, string $templateOwnerName, string $templateDirectory, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) use ($redeployVcsLogic) {
|
||||
->action(function (string $functionId, string $name, string $runtime, 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, string $templateRepositoryName, string $templateOwnerName, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) use ($redeployVcsLogic) {
|
||||
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
|
||||
|
||||
// build from template
|
||||
$template = new Document([]);
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName)) {
|
||||
$template->setAttribute('templateRepositoryName', $templateRepositoryName)
|
||||
->setAttribute('templateOwnerName', $templateOwnerName)
|
||||
->setAttribute('templateDirectory', $templateDirectory);
|
||||
$template->setAttribute('repositoryName', $templateRepositoryName)
|
||||
->setAttribute('ownerName', $templateOwnerName)
|
||||
->setAttribute('rootDirectory', $templateRootDirectory)
|
||||
->setAttribute('branch', $templateBranch);
|
||||
}
|
||||
|
||||
$installation = $dbForConsole->getDocument('vcsInstallations', $vcsInstallationId, [
|
||||
|
@ -176,6 +186,7 @@ App::post('/v1/functions')
|
|||
'$id' => $functionId,
|
||||
'execute' => $execute,
|
||||
'enabled' => $enabled,
|
||||
'live' => true,
|
||||
'logging' => $logging,
|
||||
'name' => $name,
|
||||
'runtime' => $runtime,
|
||||
|
@ -213,7 +224,7 @@ App::post('/v1/functions')
|
|||
|
||||
// Redeploy vcs logic
|
||||
if (!empty($vcsRepositoryId)) {
|
||||
$redeployVcsLogic($function, $project, $installation, $dbForProject, $template);
|
||||
$redeployVcsLogic($request, $function, $project, $installation, $dbForProject, $template);
|
||||
}
|
||||
|
||||
$functionsDomain = App::getEnv('_APP_DOMAIN_FUNCTIONS', 'disabled');
|
||||
|
@ -622,7 +633,7 @@ App::put('/v1/functions/:functionId')
|
|||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
|
||||
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
|
||||
->param('logging', true, new Boolean(), 'Do executions get logged?', true)
|
||||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.', true)
|
||||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.')
|
||||
->param('buildCommand', '', new Text('1028'), 'Build Command.', true)
|
||||
->param('installCommand', '', new Text('1028'), 'Install Command.', true)
|
||||
->param('vcsInstallationId', '', new Text(128), 'Appwrite Installation ID for vcs deployment.', true)
|
||||
|
@ -630,13 +641,14 @@ App::put('/v1/functions/:functionId')
|
|||
->param('vcsBranch', '', new Text(128), 'Production branch for the repo linked to the function', true)
|
||||
->param('vcsSilentMode', false, new Boolean(), 'Is VCS connection 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('request')
|
||||
->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 $vcsRepositoryId, string $vcsBranch, bool $vcsSilentMode, string $vcsRootDirectory, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) use ($redeployVcsLogic) {
|
||||
->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, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance, Database $dbForConsole) use ($redeployVcsLogic) {
|
||||
// TODO: If only branch changes, re-deploy
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
@ -712,6 +724,17 @@ App::put('/v1/functions/:functionId')
|
|||
$vcsRepositoryDocInternalId = $vcsRepoDoc->getInternalId();
|
||||
}
|
||||
|
||||
$live = true;
|
||||
|
||||
if (
|
||||
$function->getAttribute('entrypoint') !== $entrypoint ||
|
||||
$function->getAttribute('buildCommand') !== $buildCommand ||
|
||||
$function->getAttribute('installCommand') !== $installCommand ||
|
||||
$function->getAttribute('vcsRootDirectory') !== $vcsRootDirectory
|
||||
) {
|
||||
$live = false;
|
||||
}
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
|
||||
'execute' => $execute,
|
||||
'name' => $name,
|
||||
|
@ -719,6 +742,7 @@ App::put('/v1/functions/:functionId')
|
|||
'schedule' => $schedule,
|
||||
'timeout' => $timeout,
|
||||
'enabled' => $enabled,
|
||||
'live' => $live,
|
||||
'logging' => $logging,
|
||||
'entrypoint' => $entrypoint,
|
||||
'buildCommand' => $buildCommand,
|
||||
|
@ -736,7 +760,7 @@ App::put('/v1/functions/:functionId')
|
|||
|
||||
// Redeploy logic
|
||||
if (!$isConnected && !empty($vcsRepositoryId)) {
|
||||
$redeployVcsLogic($function, $project, $installation, $dbForProject, new Document());
|
||||
$redeployVcsLogic($request, $function, $project, $installation, $dbForProject, new Document());
|
||||
}
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
|
@ -1128,6 +1152,7 @@ App::get('/v1/functions/:functionId/deployments')
|
|||
$result->setAttribute('buildStderr', $build->getAttribute('stderr', ''));
|
||||
$result->setAttribute('buildStdout', $build->getAttribute('stdout', ''));
|
||||
$result->setAttribute('buildTime', $build->getAttribute('duration', 0));
|
||||
$result->setAttribute('size', $result->getAttribute('size', 0) + $build->getAttribute('size', 0));
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
|
@ -1173,6 +1198,8 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
$deployment->setAttribute('status', $build->getAttribute('status', 'waiting'));
|
||||
$deployment->setAttribute('buildStderr', $build->getAttribute('stderr', ''));
|
||||
$deployment->setAttribute('buildStdout', $build->getAttribute('stdout', ''));
|
||||
$deployment->setAttribute('buildTime', $build->getAttribute('duration', 0));
|
||||
$deployment->setAttribute('size', $deployment->getAttribute('size', 0) + $build->getAttribute('size', 0));
|
||||
|
||||
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
});
|
||||
|
@ -1257,11 +1284,13 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
|||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('deploymentId', '', new UID(), 'Deployment ID.')
|
||||
->param('buildId', '', new UID(), 'Build unique ID.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForConsole')
|
||||
->inject('project')
|
||||
->inject('events')
|
||||
->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) {
|
||||
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Event $events) use ($redeployVcsLogic) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
@ -1280,22 +1309,11 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
|||
throw new Exception(Exception::BUILD_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($build->getAttribute('status') !== 'failed') {
|
||||
throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed');
|
||||
}
|
||||
// TODO: Somehow set commit SHA for git deployments, and file path for manual. Redeploy should use exact same source code
|
||||
|
||||
$events
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
$installation = $dbForConsole->getDocument('vcsInstallations', $deployment->getAttribute('vcsInstallationId', ''));
|
||||
|
||||
// Retry the build
|
||||
$buildEvent = new Build();
|
||||
$buildEvent
|
||||
->setType(BUILD_TYPE_RETRY)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
$redeployVcsLogic($request, $function, $project, $installation, $dbForProject, new Document([]));
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
@ -1326,7 +1344,8 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->inject('usage')
|
||||
->inject('mode')
|
||||
->inject('queueForFunctions')
|
||||
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage, string $mode, Func $queueForFunctions) {
|
||||
->inject('geodb')
|
||||
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $events, Stats $usage, string $mode, Func $queueForFunctions, Reader $geodb) {
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
|
@ -1403,7 +1422,6 @@ App::post('/v1/functions/:functionId/executions')
|
|||
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
|
||||
|
||||
$jwt = ''; // initialize
|
||||
if (!$user->isEmpty()) { // If userId exists, generate a JWT for function
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
@ -1425,6 +1443,27 @@ App::post('/v1/functions/:functionId/executions')
|
|||
}
|
||||
}
|
||||
|
||||
$headers['x-appwrite-trigger'] = 'http';
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
|
||||
|
||||
$headers['x-appwrite-country-code'] = '';
|
||||
$headers['x-appwrite-continent-code'] = '';
|
||||
$headers['x-appwrite-continent-eu'] = 'false';
|
||||
|
||||
$ip = $headers['x-real-ip'] ?? '';
|
||||
if (!empty($ip)) {
|
||||
$record = $geodb->get($ip);
|
||||
|
||||
if ($record) {
|
||||
$eu = Config::getParam('locale-eu');
|
||||
|
||||
$headers['x-appwrite-country-code'] = $record['country']['iso_code'] ?? '';;
|
||||
$headers['x-appwrite-continent-code'] = $record['continent']['code'] ?? '';
|
||||
$headers['x-appwrite-continent-eu'] = (\in_array($record['country']['iso_code'], $eu)) ? 'true' : 'false';
|
||||
}
|
||||
}
|
||||
|
||||
$events
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executionId', $execution->getId())
|
||||
|
@ -1470,16 +1509,12 @@ App::post('/v1/functions/:functionId/executions')
|
|||
|
||||
// Appwrite vars
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_ID' => $function->getId(),
|
||||
'APPWRITE_FUNCTION_ID' => $functionId,
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_TRIGGER' => 'http',
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' => $data ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' => $jwt ?? '',
|
||||
]);
|
||||
|
||||
/** Execute function */
|
||||
|
@ -1490,7 +1525,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
version: $function->getAttribute('version'),
|
||||
body: $body,
|
||||
body: \strlen($body) > 0 ? $body : null,
|
||||
variables: $vars,
|
||||
timeout: $function->getAttribute('timeout', 0),
|
||||
image: $runtime['image'],
|
||||
|
@ -1720,6 +1755,8 @@ App::post('/v1/functions/:functionId/variables')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
|
@ -1729,13 +1766,6 @@ App::post('/v1/functions/:functionId/variables')
|
|||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setFunction($function)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
|
@ -1853,6 +1883,8 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
|
@ -1862,13 +1894,6 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setFunction($function)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
});
|
||||
|
||||
|
@ -1909,6 +1934,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
|||
|
||||
$dbForProject->deleteDocument('variables', $variable->getId());
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
|
||||
$schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
|
@ -1918,12 +1945,5 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
|
|||
|
||||
$dbForProject->deleteCachedDocument('functions', $function->getId());
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setFunction($function)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
|
|
@ -163,13 +163,15 @@ App::post('/v1/project/variables')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('projects', $project->getId());
|
||||
$functions = $dbForProject->find('functions', [
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]);
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
foreach ($functions as $function) {
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
|
@ -258,8 +260,7 @@ App::put('/v1/project/variables/:variableId')
|
|||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $key, 'project']))
|
||||
;
|
||||
->setAttribute('search', implode(' ', [$variableId, $key, 'project']));
|
||||
|
||||
try {
|
||||
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
|
||||
|
@ -267,13 +268,15 @@ App::put('/v1/project/variables/:variableId')
|
|||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('projects', $project->getId());
|
||||
$functions = $dbForProject->find('functions', [
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]);
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
foreach ($functions as $function) {
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
});
|
||||
|
@ -302,14 +305,16 @@ App::delete('/v1/project/variables/:variableId')
|
|||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$functions = $dbForProject->find('functions', [
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]);
|
||||
|
||||
foreach ($functions as $function) {
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
}
|
||||
|
||||
$dbForProject->deleteDocument('variables', $variable->getId());
|
||||
$dbForProject->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
// Stop all running runtimes with this variable
|
||||
(new Delete())
|
||||
->setType(DELETE_TYPE_RUNTIMES)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
|
|
@ -72,9 +72,10 @@ App::get('/v1/vcs/github/redirect')
|
|||
->desc('Capture installation and authorization from GitHub App')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->param('installation_id', '', new Text(256), 'GitHub installation ID')
|
||||
->param('setup_action', '', new Text(256), 'GitHub setup actuon type')
|
||||
->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.')
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->param('installation_id', '', new Text(256), 'GitHub installation ID', true)
|
||||
->param('setup_action', '', new Text(256), 'GitHub setup actuon type', true)
|
||||
->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.', true)
|
||||
->param('code', '', new Text(2048), 'OAuth2 code.', true)
|
||||
->inject('gitHub')
|
||||
->inject('user')
|
||||
|
@ -83,6 +84,10 @@ App::get('/v1/vcs/github/redirect')
|
|||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $installationId, string $setupAction, string $state, string $code, GitHub $github, Document $user, Document $project, Request $request, Response $response, Database $dbForConsole) {
|
||||
if(empty($state)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Installation requests from organisation members for the Appwrite GitHub App are currently unsupported. To proceed with the installation, login to the Appwrite Console and install the GitHub App.');
|
||||
}
|
||||
|
||||
$state = \json_decode($state, true);
|
||||
$redirect = $state['redirect'] ?? '';
|
||||
$projectId = $state['projectId'] ?? '';
|
||||
|
@ -98,6 +103,7 @@ App::get('/v1/vcs/github/redirect')
|
|||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($redirect);
|
||||
return;
|
||||
}
|
||||
|
||||
$personalSlug = '';
|
||||
|
@ -158,6 +164,8 @@ App::get('/v1/vcs/github/redirect')
|
|||
$vcsInstallation = $vcsInstallation->setAttribute('personal', $personalSlug === $owner);
|
||||
$vcsInstallation = $dbForConsole->updateDocument('vcsInstallations', $vcsInstallation->getId(), $vcsInstallation);
|
||||
}
|
||||
} else {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Installation of the Appwrite GitHub App on organization accounts is restricted to organization owners. As a member of the organization, you do not have the necessary permissions to install this GitHub App. Please contact the organization owner to create the installation from the Appwrite console.');
|
||||
}
|
||||
|
||||
$response
|
||||
|
@ -331,6 +339,8 @@ App::post('/v1/vcs/github/installations/:installationId/repositories')
|
|||
|
||||
$repository['id'] = \strval($repository['id']);
|
||||
$repository['pushedAt'] = $repository['pushed_at'];
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
|
||||
$response->dynamic(new Document($repository), Response::MODEL_REPOSITORY);
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@ use Utopia\DSN\DSN;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
@ -67,8 +68,6 @@ class BuildsV1 extends Worker
|
|||
|
||||
protected function buildDeployment(GitHub $github, Document $project, Document $function, Document $deployment, Document $template, string $SHA = '', string $targetUrl = '')
|
||||
{
|
||||
$templateRepositoryNmae = $template->getAttribute('templateRepositoryName');
|
||||
$templateOwnerName = $template->getAttribute('templateOwnerName');
|
||||
global $register;
|
||||
|
||||
$dbForProject = $this->getProjectDB($project);
|
||||
|
@ -101,116 +100,39 @@ class BuildsV1 extends Worker
|
|||
Console::error($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
|
||||
}
|
||||
|
||||
$buildId = $deployment->getAttribute('buildId', '');
|
||||
// Realtime preparation
|
||||
$allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentId' => $deployment->getId()
|
||||
]);
|
||||
|
||||
$startTime = DateTime::now();
|
||||
$durationStart = \microtime(true);
|
||||
|
||||
$buildId = $deployment->getAttribute('buildId', '');
|
||||
$build = null;
|
||||
|
||||
$isNewBuild = empty($buildId);
|
||||
|
||||
if (empty($buildId)) {
|
||||
$buildId = ID::unique();
|
||||
|
||||
$vcsInstallationId = $deployment->getAttribute('vcsInstallationId', '');
|
||||
$repositoryId = $deployment->getAttribute('vcsRepositoryId', '');
|
||||
$isVcsEnabled = $repositoryId ? true : false;
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
\var_dump("Will clone");
|
||||
$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('vcsBranch');
|
||||
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryName, $branchName, $tmpDirectory, $rootDirectory);
|
||||
\var_dump($gitCloneCommand);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('mkdir -p /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
Console::execute($gitCloneCommand, '', $stdout, $stderr);
|
||||
|
||||
// build from template
|
||||
$templateRepositoryName = $template->getAttribute('templateRepositoryName');
|
||||
$templateOwnerName = $template->getAttribute('templateOwnerName');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName)) {
|
||||
// clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
|
||||
$templateRootDirectory = $template->getAttribute('templateDirectory', '');
|
||||
var_dump($templateOwnerName . " " . $templateRepositoryName);
|
||||
$gitCloneCommandForTemplate = $github->generateGitCloneCommand($templateOwnerName, $templateRepositoryName, 'main', $tmpTemplateDirectory, $templateRootDirectory);
|
||||
var_dump("clone cmd for template " . $gitCloneCommandForTemplate);
|
||||
Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
// TODO: move template to code directory
|
||||
// Console::execute('mv FROM TO', '', $stdout, $stderr);
|
||||
|
||||
// TODO: commit and push
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
$fileName = 'code.tar.gz';
|
||||
$fileTmpName = '/tmp/builds/' . $buildId . '/code.tar.gz';
|
||||
|
||||
$deploymentId = $deployment->getId();
|
||||
$path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
$result = $deviceFunctions->move($fileTmpName, $path);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
Console::execute('rm -rf /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'path' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $path,
|
||||
'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0
|
||||
]));
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('processing', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject);
|
||||
}
|
||||
} else {
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'path' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $deployment->getAttribute('path'),
|
||||
'sourceType' => $device,
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0,
|
||||
'size' => 0
|
||||
]));
|
||||
}
|
||||
$build = $dbForProject->createDocument('builds', new Document([
|
||||
'$id' => $buildId,
|
||||
'$permissions' => [],
|
||||
'startTime' => $startTime,
|
||||
'deploymentInternalId' => $deployment->getInternalId(),
|
||||
'deploymentId' => $deployment->getId(),
|
||||
'status' => 'processing',
|
||||
'path' => '',
|
||||
'runtime' => $function->getAttribute('runtime'),
|
||||
'source' => $deployment->getAttribute('path', ''),
|
||||
'sourceType' => strtolower(App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)),
|
||||
'stdout' => '',
|
||||
'stderr' => '',
|
||||
'endTime' => null,
|
||||
'duration' => 0,
|
||||
'size' => 0
|
||||
]));
|
||||
|
||||
$deployment->setAttribute('buildId', $build->getId());
|
||||
$deployment->setAttribute('buildInternalId', $build->getInternalId());
|
||||
|
@ -219,70 +141,187 @@ class BuildsV1 extends Worker
|
|||
$build = $dbForProject->getDocument('builds', $buildId);
|
||||
}
|
||||
|
||||
/** Request the executor to build the code... */
|
||||
$build->setAttribute('status', 'building');
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('building', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject);
|
||||
}
|
||||
|
||||
/** Trigger Webhook */
|
||||
$deploymentModel = new Deployment();
|
||||
|
||||
$deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME);
|
||||
$deploymentUpdate
|
||||
->setProject($project)
|
||||
->setEvent('functions.[functionId].deployments.[deploymentId].update')
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('deploymentId', $deployment->getId())
|
||||
->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules())))
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$pools = $register->get('pools');
|
||||
$connection = $pools->get('queue')->pop();
|
||||
|
||||
$functions = new Func($connection->getResource());
|
||||
$functions
|
||||
->from($deploymentUpdate)
|
||||
->trigger();
|
||||
|
||||
$connection->reclaim();
|
||||
|
||||
/** Trigger Realtime */
|
||||
$allEvents = Event::generateEvents('functions.[functionId].deployments.[deploymentId].update', [
|
||||
'functionId' => $function->getId(),
|
||||
'deploymentId' => $deployment->getId()
|
||||
]);
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $build,
|
||||
project: $project
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
|
||||
$source = $deployment->getAttribute('path');
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
\var_dump("Changing source");
|
||||
$source = $path;
|
||||
}
|
||||
|
||||
$vars = array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
|
||||
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
|
||||
return $carry;
|
||||
}, []);
|
||||
$source = $deployment->getAttribute('path', '');
|
||||
$vcsInstallationId = $deployment->getAttribute('vcsInstallationId', '');
|
||||
$repositoryId = $deployment->getAttribute('vcsRepositoryId', '');
|
||||
$isVcsEnabled = $repositoryId ? true : false;
|
||||
$owner = '';
|
||||
$repositoryName = '';
|
||||
$branchName = '';
|
||||
|
||||
try {
|
||||
if ($isNewBuild) {
|
||||
if ($isVcsEnabled) {
|
||||
$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('vcsBranch');
|
||||
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryName, $branchName, $tmpDirectory, $rootDirectory);
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
Console::execute('mkdir -p /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to clone code repository: ' . $stderr);
|
||||
}
|
||||
|
||||
// Build from template
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateBranch = $template->getAttribute('branch', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
|
||||
$gitCloneCommandForTemplate = $github->generateGitCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to clone template repository: ' . $stderr);
|
||||
}
|
||||
|
||||
// Ensure directories
|
||||
Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr);
|
||||
Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
|
||||
// Merge template into user repo
|
||||
Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
|
||||
|
||||
// Commit and push
|
||||
$exit = Console::execute('git config --global user.email "security@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . $function->getAttribute('name', '') . '\' function" && git push origin ' . $branchName, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to push code repository: ' . $stderr);
|
||||
}
|
||||
|
||||
$exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
throw new \Exception('Unable to get commit SHA: ' . $stderr);
|
||||
}
|
||||
|
||||
$SHA = \trim($stdout);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
$fileName = 'code.tar.gz';
|
||||
$fileTmpName = '/tmp/builds/' . $buildId . '/code.tar.gz';
|
||||
|
||||
$path = $deviceFunctions->getPath($deployment->getId() . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
$result = $deviceFunctions->move($fileTmpName, $path);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception("Unable to move file");
|
||||
}
|
||||
|
||||
Console::execute('rm -rf /tmp/builds/' . $buildId, '', $stdout, $stderr);
|
||||
|
||||
$source = $path;
|
||||
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('processing', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Request the executor to build the code... */
|
||||
$build->setAttribute('status', 'building');
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('building', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
|
||||
/** Trigger Webhook */
|
||||
$deploymentModel = new Deployment();
|
||||
|
||||
$deploymentUpdate = new Event(Event::WEBHOOK_QUEUE_NAME, Event::WEBHOOK_CLASS_NAME);
|
||||
$deploymentUpdate
|
||||
->setProject($project)
|
||||
->setEvent('functions.[functionId].deployments.[deploymentId].update')
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('deploymentId', $deployment->getId())
|
||||
->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules())))
|
||||
->trigger();
|
||||
|
||||
/** Trigger Functions */
|
||||
$pools = $register->get('pools');
|
||||
$connection = $pools->get('queue')->pop();
|
||||
|
||||
$functions = new Func($connection->getResource());
|
||||
$functions
|
||||
->from($deploymentUpdate)
|
||||
->trigger();
|
||||
|
||||
$connection->reclaim();
|
||||
|
||||
/** Trigger Realtime */
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $build,
|
||||
project: $project
|
||||
);
|
||||
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
|
||||
$vars = [];
|
||||
|
||||
// global vars
|
||||
$vars = \array_merge($vars, \array_reduce($dbForProject->find('variables', [
|
||||
Query::equal('resourceType', ['project']),
|
||||
Query::limit(APP_LIMIT_SUBQUERY)
|
||||
]), function (array $carry, Document $var) {
|
||||
$carry[$var->getAttribute('key')] = $var->getAttribute('value') ?? '';
|
||||
return $carry;
|
||||
}, []));
|
||||
|
||||
// Function vars
|
||||
$vars = \array_merge($vars, array_reduce($function->getAttribute('vars', []), function (array $carry, Document $var) {
|
||||
$carry[$var->getAttribute('key')] = $var->getAttribute('value');
|
||||
return $carry;
|
||||
}, []));
|
||||
|
||||
// Appwrite vars
|
||||
$vars = \array_merge($vars, [
|
||||
'APPWRITE_FUNCTION_ID' => $function->getId(),
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
]);
|
||||
|
||||
$command = '';
|
||||
|
||||
if (!empty($deployment->getAttribute('installCommand', ''))) {
|
||||
|
@ -298,61 +337,79 @@ class BuildsV1 extends Worker
|
|||
|
||||
$response = null;
|
||||
|
||||
// TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary
|
||||
Co\run(function () use ($project, $deployment, &$response, $source, $function, $runtime, $vars, $command, &$build, $dbForProject, $allEvents) {
|
||||
Co::join([
|
||||
Co\go(function () use ($project, $deployment, &$response, &$build, $dbForProject, $allEvents) {
|
||||
$this->executor->getLogs(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) {
|
||||
if ($response === null) {
|
||||
$build = $build->setAttribute('stdout', $build->getAttribute('stdout', '') . $logs);
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build);
|
||||
$err = null;
|
||||
|
||||
/**
|
||||
* Send realtime Event
|
||||
*/
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $build,
|
||||
project: $project
|
||||
);
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
// TODO: Remove run() wrapper when switching to new utopia queue. That should be done on Swoole adapter in the libary
|
||||
Co\run(function () use ($project, $deployment, &$response, $source, $function, $runtime, $vars, $command, &$build, $dbForProject, $allEvents, &$err) {
|
||||
Co::join([
|
||||
Co\go(function () use (&$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) {
|
||||
try {
|
||||
$response = $this->executor->createRuntime(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
source: $source,
|
||||
version: $function->getAttribute('version'),
|
||||
image: $runtime['image'],
|
||||
remove: true,
|
||||
entrypoint: $deployment->getAttribute('entrypoint'),
|
||||
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
|
||||
variables: $vars,
|
||||
command: 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'
|
||||
);
|
||||
} catch (Exception $error) {
|
||||
$err = $error;
|
||||
}
|
||||
}),
|
||||
Co\go(function () use (&$response, $project, $deployment, $source, $function, $runtime, $vars, $command) {
|
||||
$response = $this->executor->createRuntime(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
source: $source,
|
||||
version: $function->getAttribute('version'),
|
||||
image: $runtime['image'],
|
||||
remove: true,
|
||||
entrypoint: $deployment->getAttribute('entrypoint'),
|
||||
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
|
||||
variables: $vars,
|
||||
command: 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'
|
||||
);
|
||||
Co\go(function () use ($project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) {
|
||||
try {
|
||||
$this->executor->getLogs(
|
||||
projectId: $project->getId(),
|
||||
deploymentId: $deployment->getId(),
|
||||
callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) {
|
||||
\var_dump("Got logs");
|
||||
if ($response === null) {
|
||||
$build = $build->setAttribute('stdout', $build->getAttribute('stdout', '') . $logs);
|
||||
$build = $dbForProject->updateDocument('builds', $build->getId(), $build);
|
||||
|
||||
/**
|
||||
* Send realtime Event
|
||||
*/
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
event: $allEvents[0],
|
||||
payload: $build,
|
||||
project: $project
|
||||
);
|
||||
Realtime::send(
|
||||
projectId: 'console',
|
||||
payload: $build->getArrayCopy(),
|
||||
events: $allEvents,
|
||||
channels: $target['channels'],
|
||||
roles: $target['roles']
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (Exception $error) {
|
||||
if (empty($err)) {
|
||||
$err = $error;
|
||||
}
|
||||
}
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
if ($err) {
|
||||
throw $err;
|
||||
}
|
||||
|
||||
$endTime = DateTime::now();
|
||||
$durationEnd = \microtime(true);
|
||||
|
||||
/** Update the build document */
|
||||
$build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime'])));
|
||||
$build->setAttribute('endTime', $endTime);
|
||||
$build->setAttribute('duration', \intval(\ceil($response['duration'])));
|
||||
$build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart)));
|
||||
$build->setAttribute('status', 'ready');
|
||||
$build->setAttribute('path', $response['path']);
|
||||
$build->setAttribute('size', $response['size']);
|
||||
|
@ -360,18 +417,16 @@ class BuildsV1 extends Worker
|
|||
$build->setAttribute('stdout', $response['stdout']);
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject);
|
||||
$this->runGitAction('ready', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
|
||||
/* Also update the deployment buildTime */
|
||||
$deployment->setAttribute('buildTime', $response['duration']);
|
||||
|
||||
Console::success("Build id: $buildId created");
|
||||
|
||||
/** Set auto deploy */
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
$function->setAttribute('deploymentInternalId', $deployment->getInternalId());
|
||||
$function->setAttribute('deployment', $deployment->getId());
|
||||
$function->setAttribute('live', true);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
}
|
||||
|
||||
|
@ -384,7 +439,6 @@ class BuildsV1 extends Worker
|
|||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
|
||||
|
||||
|
||||
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
} catch (\Throwable $th) {
|
||||
$endTime = DateTime::now();
|
||||
|
@ -396,7 +450,7 @@ class BuildsV1 extends Worker
|
|||
Console::error($th->getMessage());
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('failed', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject);
|
||||
$this->runGitAction('failed', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
|
||||
}
|
||||
} finally {
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
@ -436,7 +490,7 @@ 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)
|
||||
protected function runGitAction(string $status, GitHub $github, string $SHA, string $owner, string $repositoryName, string $targetUrl, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole)
|
||||
{
|
||||
if ($function->getAttribute('vcsSilentMode', false) === true) {
|
||||
return;
|
||||
|
@ -470,11 +524,31 @@ class BuildsV1 extends Worker
|
|||
|
||||
// TODO: Fix race condition
|
||||
if (!empty($commentId)) {
|
||||
$retries = 0;
|
||||
|
||||
while ($retries < 10) {
|
||||
$retries++;
|
||||
|
||||
try {
|
||||
$dbForConsole->createDocument('vcsCommentLocks', new Document([
|
||||
'$id' => $commentId
|
||||
]));
|
||||
break;
|
||||
} catch (Exception $err) {
|
||||
if ($retries >= 9) {
|
||||
throw $err;
|
||||
}
|
||||
}
|
||||
|
||||
\sleep(1);
|
||||
}
|
||||
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $commentId));
|
||||
\sleep(5);
|
||||
$comment->addBuild($project, $function, $status, $deployment->getId());
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment());
|
||||
|
||||
$dbForConsole->deleteDocument('vcsCommentLocks', $commentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,20 +149,19 @@ Server::setResource('execute', function () {
|
|||
'APPWRITE_FUNCTION_ID' => $functionId,
|
||||
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
|
||||
'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId,
|
||||
'APPWRITE_FUNCTION_TRIGGER' => $trigger,
|
||||
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
|
||||
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
|
||||
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT' => $event ?? '',
|
||||
'APPWRITE_FUNCTION_EVENT_DATA' => $eventData ?? '',
|
||||
'APPWRITE_FUNCTION_DATA' => $data ?? '',
|
||||
'APPWRITE_FUNCTION_USER_ID' => $user->getId() ?? '',
|
||||
'APPWRITE_FUNCTION_JWT' => $jwt ?? '',
|
||||
]);
|
||||
|
||||
$body = $vars['APPWRITE_FUNCTION_EVENT_DATA'] ?? '';
|
||||
$headers['x-appwrite-trigger'] = $trigger;
|
||||
$headers['x-appwrite-event'] = $event ?? '';
|
||||
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
|
||||
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
|
||||
|
||||
$body = $eventData ?? '';
|
||||
if (empty($body)) {
|
||||
$body = $vars['APPWRITE_FUNCTION_DATA'] ?? '';
|
||||
$body = $data ?? '';
|
||||
}
|
||||
|
||||
/** Execute function */
|
||||
|
@ -173,7 +172,7 @@ Server::setResource('execute', function () {
|
|||
projectId: $project->getId(),
|
||||
deploymentId: $deploymentId,
|
||||
version: $function->getAttribute('version'),
|
||||
body: $body,
|
||||
body: \strlen($body) > 0 ? $body : null,
|
||||
variables: $vars,
|
||||
timeout: $function->getAttribute('timeout', 0),
|
||||
image: $runtime['image'],
|
||||
|
|
4
composer.lock
generated
4
composer.lock
generated
|
@ -2705,7 +2705,7 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/vcs.git",
|
||||
"reference": "df56d0eecf308e0c8b1e86c6801bb44c7c363052"
|
||||
"reference": "6131630cdc48f0fde2207fc0efcce503e7967272"
|
||||
},
|
||||
"require": {
|
||||
"adhocore/jwt": "^1.1",
|
||||
|
@ -2750,7 +2750,7 @@
|
|||
"utopia",
|
||||
"vcs"
|
||||
],
|
||||
"time": "2023-06-19T16:54:01+00:00"
|
||||
"time": "2023-06-22T06:40:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/websocket",
|
||||
|
|
|
@ -762,7 +762,7 @@ services:
|
|||
hostname: exc1
|
||||
<<: *x-logging
|
||||
stop_signal: SIGINT
|
||||
image: meldiron/executor:0.3.2
|
||||
image: meldiron/executor:0.3.3
|
||||
networks:
|
||||
- appwrite
|
||||
- runtimes
|
||||
|
|
|
@ -49,6 +49,12 @@ class Func extends Model
|
|||
'default' => true,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('live', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Is function live (deployed with latest config)?',
|
||||
'default' => true,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('logging', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Function logging.',
|
||||
|
|
|
@ -156,7 +156,7 @@ class Executor
|
|||
*
|
||||
* @param string $projectId
|
||||
* @param string $deploymentId
|
||||
* @param string $payload
|
||||
* @param string $body
|
||||
* @param array $variables
|
||||
* @param int $timeout
|
||||
* @param string $image
|
||||
|
@ -169,7 +169,7 @@ class Executor
|
|||
public function createExecution(
|
||||
string $projectId,
|
||||
string $deploymentId,
|
||||
string $body,
|
||||
?string $body,
|
||||
array $variables,
|
||||
int $timeout,
|
||||
string $image,
|
||||
|
|
Loading…
Reference in a new issue