From 5d2d14df6ea43a9889f873f02720925699b4781e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Mon, 29 May 2023 13:51:03 +0200 Subject: [PATCH] Fix bugs with comment, improve git checks logic --- app/controllers/api/vcs.php | 54 +++++++++++++++++------ app/workers/builds.php | 87 ++++++++++++++++++++----------------- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 207912db5b..a6a786670c 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -232,7 +232,7 @@ App::get('v1/vcs/github/installations/:installationId/repositories/:repositoryId ]), Response::MODEL_BRANCH_LIST); }); -$createGitDeployments = function (array $vcsRepos, string $branchName, string $SHA, string $commentId, Database $dbForConsole, callable $getProjectDB, Request $request) { +$createGitDeployments = function (GitHub $github, string $installationId, array $vcsRepos, string $branchName, string $SHA, string $commentId, Database $dbForConsole, callable $getProjectDB, Request $request) { foreach ($vcsRepos as $resource) { $resourceType = $resource->getAttribute('resourceType'); @@ -242,7 +242,6 @@ $createGitDeployments = function (array $vcsRepos, string $branchName, string $S $dbForProject = $getProjectDB($project); $functionId = $resource->getAttribute('resourceId'); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); $deploymentId = ID::unique(); $vcsRepoId = $resource->getId(); $vcsRepoInternalId = $resource->getInternalId(); @@ -255,18 +254,31 @@ $createGitDeployments = function (array $vcsRepos, string $branchName, string $S $activate = true; } - if (empty($commentId)) { - $latestDeployment = Authorization::skip(fn () => $dbForProject->findOne('deployments', [ - Query::equal('vcsRepositoryId', [$vcsRepoId]), - Query::equal('branch', [$branchName]), - Query::equal('resourceType', ['functions']), - Query::orderDesc('$createdAt'), - ])); + $latestDeployment = Authorization::skip(fn () => $dbForProject->findOne('deployments', [ + Query::equal('vcsRepositoryId', [$vcsRepoId]), + Query::equal('branch', [$branchName]), + Query::equal('resourceType', ['functions']), + Query::orderDesc('$createdAt'), + ])); + + $latestCommentId = $commentId ?? ''; + + if (empty($latestCommentId)) { + // Empty comment ID is push event. We try to take ID from last deplyoment if ($latestDeployment !== false && !$latestDeployment->isEmpty()) { - $commentId = $latestDeployment->getAttribute('vcsCommentId', ''); + $latestCommentId = $latestDeployment->getAttribute('vcsCommentId', ''); + } + } else { + // Known comment ID is pull request event. If deployment exists already, we skip + if ($latestDeployment !== false && !$latestDeployment->isEmpty()) { + $latestDeployment->setAttribute('vcsCommentId', $latestCommentId); + Authorization::skip(fn () => $dbForProject->updateDocument('deployments', $latestDeployment->getId(), $latestDeployment)); + continue; } } + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $deployment = $dbForProject->createDocument('deployments', new Document([ '$id' => $deploymentId, '$permissions' => [ @@ -284,7 +296,7 @@ $createGitDeployments = function (array $vcsRepos, string $branchName, string $S 'vcsInstallationInternalId' => $vcsInstallationInternalId, 'vcsRepositoryId' => $vcsRepoId, 'vcsRepositoryInternalId' => $vcsRepoInternalId, - 'vcsCommentId' => $commentId, + 'vcsCommentId' => $latestCommentId, 'branch' => $branchName, 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), 'activate' => $activate, @@ -293,6 +305,19 @@ $createGitDeployments = function (array $vcsRepos, string $branchName, string $S // TODO: Figure out port $targetUrl = $request->getProtocol() . '://' . $request->getHostname() . ":3000/console/project-$projectId/functions/function-$functionId"; + if (!empty($SHA)) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Starting...'; + + $repositoryId = $resource->getAttribute('repositoryId'); + $repositoryName = $github->getRepositoryName($repositoryId); + $owner = $github->getOwnerName($installationId); + $github->updateCommitStatus($repositoryName, $SHA, $owner, 'pending', $message, $targetUrl, $name); + } + + $buildEvent = new Build(); $buildEvent ->setType(BUILD_TYPE_DEPLOYMENT) @@ -332,13 +357,15 @@ App::post('/v1/vcs/github/incomingwebhook') $SHA = $parsedPayload["SHA"]; $owner = $parsedPayload["owner"]; + $github->initialiseVariables($installationId, $privateKey, $githubAppId); + //find functionId from functions table $vcsRepos = $dbForConsole->find('vcs_repos', [ Query::equal('repositoryId', [$repositoryId]), Query::limit(100), ]); - $createGitDeployments($vcsRepos, $branchName, $SHA, '', $dbForConsole, $getProjectDB, $request); + $createGitDeployments($github, $installationId, $vcsRepos, $branchName, $SHA, '', $dbForConsole, $getProjectDB, $request); } elseif ($event == $github::EVENT_INSTALLATION) { if ($parsedPayload["action"] == "deleted") { // TODO: Use worker for this job instead (update function as well) @@ -370,6 +397,7 @@ App::post('/v1/vcs/github/incomingwebhook') $pullRequestNumber = $parsedPayload["pullRequestNumber"]; $repositoryName = $parsedPayload["repositoryName"]; $owner = $parsedPayload["owner"]; + $github->initialiseVariables($installationId, $privateKey, $githubAppId); $vcsRepos = $dbForConsole->find('vcs_repos', [ @@ -420,7 +448,7 @@ App::post('/v1/vcs/github/incomingwebhook') $commentId = $github->createComment($owner, $repositoryName, $pullRequestNumber, $comment->generateComment()); } - $createGitDeployments($vcsRepos, $branchName, '', $commentId, $dbForConsole, $getProjectDB, $request); + $createGitDeployments($github, $installationId, $vcsRepos, $branchName, '', $commentId, $dbForConsole, $getProjectDB, $request); } } } diff --git a/app/workers/builds.php b/app/workers/builds.php index 04858d29e3..d094d8c802 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -15,6 +15,7 @@ use Utopia\Database\ID; use Utopia\DSN\DSN; use Utopia\Database\Document; use Utopia\Config\Config; +use Utopia\Database\Database; use Utopia\Storage\Storage; use Utopia\Database\Validator\Authorization; use Utopia\VCS\Adapter\Git\GitHub; @@ -110,8 +111,7 @@ class BuildsV1 extends Worker $vcsRepos = Authorization::skip(fn () => $dbForConsole ->getDocument('vcs_repos', $vcsRepoId)); $repositoryId = $vcsRepos->getAttribute('repositoryId'); - $vcsInstallations = Authorization::skip(fn () => $dbForConsole - ->getDocument('vcs_installations', $vcsInstallationId)); + $vcsInstallations = Authorization::skip(fn () => $dbForConsole->getDocument('vcs_installations', $vcsInstallationId)); $installationId = $vcsInstallations->getAttribute('installationId'); $privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY'); @@ -159,15 +159,9 @@ class BuildsV1 extends Worker 'endTime' => null, 'duration' => 0 ])); - if ($SHA !== "" && $owner !== "") { - $github->updateCommitStatus($repositoryName, $SHA, $owner, "pending", "Deployment is being processed..", $targetUrl, "Appwrite Deployment"); - } - $commentId = $deployment->getAttribute('vcsCommentId'); - if ($commentId) { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $function, 'processing', $deployment->getId()); - $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); + + if ($isVcsEnabled) { + $this->runGitAction('processing', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject); } } else { $build = $dbForProject->createDocument('builds', new Document([ @@ -201,13 +195,7 @@ class BuildsV1 extends Worker $build = $dbForProject->updateDocument('builds', $buildId, $build); if ($isVcsEnabled) { - $commentId = $deployment->getAttribute('vcsCommentId'); - if ($commentId) { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $function, 'building', $deployment->getId()); - $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); - } + $this->runGitAction('building', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject); } /** Trigger Webhook */ @@ -307,17 +295,7 @@ class BuildsV1 extends Worker $build->setAttribute('stdout', $response['stdout']); if ($isVcsEnabled) { - if ($SHA !== "" && $owner !== "") { - $github->updateCommitStatus($repositoryName, $SHA, $owner, "success", "Deployment is successful!", $targetUrl, "Appwrite Deployment"); - } - - $commentId = $deployment->getAttribute('vcsCommentId'); - if ($commentId) { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $function, 'ready', $deployment->getId()); - $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); - } + $this->runGitAction('ready', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject); } /* Also update the deployment buildTime */ @@ -353,18 +331,7 @@ class BuildsV1 extends Worker Console::error($th->getMessage()); if ($isVcsEnabled) { - $status = 'failed'; - if ($SHA !== "" && $owner !== "") { - $github->updateCommitStatus($repositoryName, $SHA, $owner, "failure", "Deployment failed.", $targetUrl, "Appwrite Deployment"); - } - - $commentId = $deployment->getAttribute('vcsCommentId'); - if ($commentId) { - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $function, 'failed', $deployment->getId()); - $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); - } + $this->runGitAction('failed', $github, $SHA, $owner, $repositoryName, $targetUrl, $project, $function, $deployment->getId(), $dbForProject); } } finally { $build = $dbForProject->updateDocument('builds', $buildId, $build); @@ -404,6 +371,44 @@ 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) + { + $deployment = $dbForProject->getDocument('deployments', $deploymentId); + $commentId = $deployment->getAttribute('vcsCommentId', ''); + + if (empty($commentId)) { + return; + } + + if (!empty($SHA)) { + $message = match ($status) { + 'ready' => 'Build succeeded.', + 'failed' => 'Build failed.', + 'processing' => 'Building...', + default => $status + }; + + $state = match ($status) { + 'ready' => 'success', + 'failed' => 'failure', + 'processing' => 'pending', + default => $status + }; + + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + + $name = "{$functionName} ({$projectName})"; + + $github->updateCommitStatus($repositoryName, $SHA, $owner, $state, $message, $targetUrl, $name); + } + + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); + $comment->addBuild($project, $function, $status, $deployment->getId()); + $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); + } + public function shutdown(): void { }