1
0
Fork 0
mirror of synced 2024-07-01 20:50:49 +12:00

Fix manual and VCS redeploys

This commit is contained in:
Jake Barnby 2023-08-19 02:15:47 -04:00
parent 2404352f3c
commit 9b2226b243
No known key found for this signature in database
GPG key ID: C437A8CC85B96E9C
2 changed files with 129 additions and 127 deletions

View file

@ -1282,12 +1282,13 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, GitHub $github, Event $events) use ($redeployVcs) {
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
@ -1298,11 +1299,22 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
throw new Exception(Exception::BUILD_NOT_FOUND);
}
// TODO: Somehow set commit SHA & ownerName & repoName for git deployments, and file path for manual. Redeploy should use exact same source code
$deployment = $dbForProject->createDocument('deployments', $deployment->setAttributes([
'$id' => ID::unique(),
'buildId' => '',
'buildInternalId' => '',
'entrypoint' => $function->getAttribute('entrypoint'),
'commands' => $function->getAttribute('commands', ''),
]));
$installation = $dbForConsole->getDocument('installations', $deployment->getAttribute('installationId', ''));
$buildEvent = new Build();
$redeployVcs($request, $function, $project, $installation, $dbForProject, new Document([]), $github);
$buildEvent
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setProject($project)
->trigger();
$events
->setParam('functionId', $function->getId())
@ -1513,13 +1525,13 @@ App::post('/v1/functions/:functionId/executions')
$executionResponse = $executor->createExecution(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
version: $function->getAttribute('version'),
body: \strlen($body) > 0 ? $body : null,
variables: $vars,
timeout: $function->getAttribute('timeout', 0),
image: $runtime['image'],
source: $build->getAttribute('path', ''),
entrypoint: $deployment->getAttribute('entrypoint', ''),
version: $function->getAttribute('version'),
path: $path,
method: $method,
headers: $headers,

View file

@ -113,11 +113,10 @@ class BuildsV1 extends Worker
$durationStart = \microtime(true);
$buildId = $deployment->getAttribute('buildId', '');
$build = null;
$isNewBuild = empty($buildId);
if (empty($buildId)) {
if ($isNewBuild) {
$buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
@ -150,135 +149,131 @@ class BuildsV1 extends Worker
$isVcsEnabled = $providerRepositoryId ? true : false;
$owner = '';
$repositoryName = '';
$branchName = '';
if ($isVcsEnabled) {
$installation = $dbForConsole->getDocument('installations', $installationId);
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
}
try {
if ($isNewBuild) {
if ($isVcsEnabled) {
$installation = $dbForConsole->getDocument('installations', $installationId);
$providerInstallationId = $installation->getAttribute('providerInstallationId');
if ($isNewBuild && $isVcsEnabled) {
$tmpDirectory = '/tmp/builds/' . $buildId . '/code';
$rootDirectory = $function->getAttribute('providerRootDirectory', '');
$rootDirectory = \rtrim($rootDirectory, '/');
$rootDirectory = \ltrim($rootDirectory, '.');
$rootDirectory = \ltrim($rootDirectory, '/');
$privateKey = App::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('_APP_VCS_GITHUB_APP_ID');
$owner = $github->getOwnerName($providerInstallationId);
$repositoryName = $github->getRepositoryName($providerRepositoryId);
$tmpDirectory = '/tmp/builds/' . $buildId . '/code';
$rootDirectory = $function->getAttribute('providerRootDirectory', '');
$rootDirectory = \rtrim($rootDirectory, '/');
$rootDirectory = \ltrim($rootDirectory, '.');
$rootDirectory = \ltrim($rootDirectory, '/');
$cloneOwner = $deployment->getAttribute('providerRepositoryOwner', $owner);
$cloneRepository = $deployment->getAttribute('providerRepositoryName', $repositoryName);
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$branchName = $deployment->getAttribute('providerBranch');
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $branchName, $tmpDirectory, $rootDirectory);
$stdout = '';
$stderr = '';
Console::execute('mkdir -p /tmp/builds/' . $buildId, '', $stdout, $stderr);
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
$owner = $github->getOwnerName($providerInstallationId);
$repositoryName = $github->getRepositoryName($providerRepositoryId);
if ($exit !== 0) {
throw new \Exception('Unable to clone code repository: ' . $stderr);
}
$cloneOwner = $deployment->getAttribute('providerRepositoryOwner', $owner);
$cloneRepository = $deployment->getAttribute('providerRepositoryName', $repositoryName);
// Build from template
$templateRepositoryName = $template->getAttribute('repositoryName', '');
$templateOwnerName = $template->getAttribute('ownerName', '');
$templateBranch = $template->getAttribute('branch', '');
$branchName = $deployment->getAttribute('providerBranch');
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $branchName, $tmpDirectory, $rootDirectory);
$stdout = '';
$stderr = '';
Console::execute('mkdir -p /tmp/builds/' . $buildId, '', $stdout, $stderr);
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
$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->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory);
$exit = Console::execute($gitCloneCommandForTemplate, '', $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', '');
// Ensure directories
Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr);
Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
// Merge template into user repo
Console::execute('cp -rfn ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/* ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
// Clone template repo
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $tmpTemplateDirectory, $templateRootDirectory);
$exit = Console::execute($gitCloneCommandForTemplate, '', $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 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 vcs commit SHA: ' . $stderr);
}
$providerCommitHash = \trim($stdout);
$authorUrl = "https://github.com/$cloneOwner";
$deployment->setAttribute('providerCommitHash', $providerCommitHash ?? '');
$deployment->setAttribute('providerCommitAuthorUrl', $authorUrl);
$deployment->setAttribute('providerCommitAuthor', 'Appwrite');
$deployment->setAttribute('providerCommitMessage', "Create '" . $function->getAttribute('name', '') . "' function");
$deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash");
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
/**
* 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']
);
if ($exit !== 0) {
throw new \Exception('Unable to push code repository: ' . $stderr);
}
Console::execute('tar --exclude code.tar.gz -czf /tmp/builds/' . $buildId . '/code.tar.gz -C /tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
$exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $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");
if ($exit !== 0) {
throw new \Exception('Unable to get vcs commit SHA: ' . $stderr);
}
Console::execute('rm -rf /tmp/builds/' . $buildId, '', $stdout, $stderr);
$providerCommitHash = \trim($stdout);
$authorUrl = "https://github.com/$cloneOwner";
$source = $path;
$deployment->setAttribute('providerCommitHash', $providerCommitHash ?? '');
$deployment->setAttribute('providerCommitAuthorUrl', $authorUrl);
$deployment->setAttribute('providerCommitAuthor', 'Appwrite');
$deployment->setAttribute('providerCommitMessage', "Create '" . $function->getAttribute('name', '') . "' function");
$deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash");
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
if ($isVcsEnabled) {
$this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
}
/**
* 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']
);
}
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));
$this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
}
/** Request the executor to build the code... */
@ -369,11 +364,11 @@ class BuildsV1 extends Worker
Co\go(function () use (&$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) {
try {
$response = $this->executor->createRuntime(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
projectId: $project->getId(),
source: $source,
version: $function->getAttribute('version'),
image: $runtime['image'],
version: $function->getAttribute('version'),
remove: true,
entrypoint: $deployment->getAttribute('entrypoint'),
destination: APP_STORAGE_BUILDS . "/app-{$project->getId()}",
@ -387,8 +382,8 @@ class BuildsV1 extends Worker
Co\go(function () use ($project, $deployment, &$response, &$build, $dbForProject, $allEvents, &$err) {
try {
$this->executor->getLogs(
projectId: $project->getId(),
deploymentId: $deployment->getId(),
projectId: $project->getId(),
callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) {
if ($response === null) {
$build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs);
@ -472,6 +467,8 @@ class BuildsV1 extends Worker
$build->setAttribute('status', 'failed');
$build->setAttribute('logs', $th->getMessage());
Console::error($th->getMessage());
Console::error($th->getFile() . ':' . $th->getLine());
Console::error($th->getTraceAsString());
if ($isVcsEnabled) {
$this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
@ -510,7 +507,7 @@ class BuildsV1 extends Worker
->trigger();
}
protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole)
protected function runGitAction(string $status, GitHub $github, string $providerCommitHash, string $owner, string $repositoryName, Document $project, Document $function, string $deploymentId, Database $dbForProject, Database $dbForConsole): void
{
if ($function->getAttribute('providerSilentMode', false) === true) {
return;
@ -551,7 +548,7 @@ class BuildsV1 extends Worker
if (!empty($commentId)) {
$retries = 0;
while ($retries < 10) {
while (true) {
$retries++;
try {
@ -563,27 +560,20 @@ class BuildsV1 extends Worker
if ($retries >= 9) {
throw $err;
}
}
\sleep(1);
\sleep(1);
}
}
// Wrap in try/catch to ensure lock file gets deleted
$error = null;
// Wrap in try/finally to ensure lock file gets deleted
try {
$comment = new Comment();
$comment->parseComment($github->getComment($owner, $repositoryName, $commentId));
$comment->addBuild($project, $function, $status, $deployment->getId(), ['type' => 'logs']);
$github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment());
} catch (\Exception $e) {
$error = $e;
} finally {
$dbForConsole->deleteDocument('vcsCommentLocks', $commentId);
}
if (!empty($error)) {
throw $error;
}
}
}