1
0
Fork 0
mirror of synced 2024-06-30 04:00:34 +12:00

log streaming, locking GH comments, execution headers, locale header, redeploy logic

This commit is contained in:
Matej Bačo 2023-06-22 12:59:41 +02:00
parent 399847d7ea
commit cc06a4bdaa
14 changed files with 450 additions and 318 deletions

View file

@ -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

View file

@ -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();
});

View file

@ -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();
});

View file

@ -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);
});

View file

@ -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);
}
}

View file

@ -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
View file

@ -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",

View file

@ -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

View file

@ -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.',

View file

@ -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,