diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 9021c6c518..32e12a4932 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -113,14 +113,15 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId if ($latestComment !== false && !$latestComment->isEmpty()) { $latestCommentId = $latestComment->getAttribute('providerCommentId', ''); + $comment = new Comment(); $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $resource, $commentStatus, $deploymentId, $action); + $comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', ''); $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); } else { $comment = new Comment(); - $comment->addBuild($project, $resource, $commentStatus, $deploymentId, $action); + $comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', ''); $latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment())); if (!empty($latestCommentId)) { @@ -157,7 +158,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $latestCommentId = $comment->getAttribute('providerCommentId', ''); $comment = new Comment(); $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $resource, $commentStatus, $deploymentId, $action); + $comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', ''); $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); } diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index bf598dd9d1..a528a14c02 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -22,6 +22,7 @@ use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Logger\Log; use Utopia\Platform\Action; @@ -690,28 +691,6 @@ class Builds extends Action $build = $dbForProject->updateDocument('builds', $buildId, $build); - if ($isVcsEnabled) { - $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForConsole); - } - - Console::success("Build id: $buildId created"); - - /** Set auto deploy */ - if ($deployment->getAttribute('activate') === true) { - $resource->setAttribute('deploymentInternalId', $deployment->getInternalId()); - $resource->setAttribute('live', true); - switch ($resource->getCollection()) { - case 'functions': - $resource->setAttribute('deployment', $deployment->getId()); - $resource = $dbForProject->updateDocument('functions', $resource->getId(), $resource); - break; - case 'sites': - $resource->setAttribute('deploymentId', $deployment->getId()); - $resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource); - break; - } - } - // Preview deployments for sites if ($resource->getCollection() === 'sites') { $ruleId = ID::unique(); @@ -737,6 +716,28 @@ class Builds extends Action ); } + if ($isVcsEnabled) { + $this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForConsole); + } + + Console::success("Build id: $buildId created"); + + /** Set auto deploy */ + if ($deployment->getAttribute('activate') === true) { + $resource->setAttribute('deploymentInternalId', $deployment->getInternalId()); + $resource->setAttribute('live', true); + switch ($resource->getCollection()) { + case 'functions': + $resource->setAttribute('deployment', $deployment->getId()); + $resource = $dbForProject->updateDocument('functions', $resource->getId(), $resource); + break; + case 'sites': + $resource->setAttribute('deploymentId', $deployment->getId()); + $resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource); + break; + } + } + if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') { Console::info('Build has been canceled'); return; @@ -976,9 +977,34 @@ class Builds extends Action // Wrap in try/finally to ensure lock file gets deleted try { + $resourceType = match($resource->getCollection()) { + 'functions' => 'function', + 'sites' => 'site', + default => throw new \Exception('Invalid resource type') + }; + + $rule = Authorization::skip(fn () => $dbForConsole->findOne('rules', [ + Query::equal("projectInternalId", [$project->getInternalId()]), + Query::equal("resourceType", ["deployment"]), + Query::equal("resourceInternalId", [$deployment->getInternalId()]) + ])); + + $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; + $previewUrl = match($resource->getCollection()) { + 'functions' => '', + 'sites' => !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : '', + default => throw new \Exception('Invalid resource type') + }; + + $previweQrCode = match($resource->getCollection()) { + 'functions' => '', + 'sites' => 'https://cloud.appwrite.io/v1/avatars/qr?text=' . $previewUrl, + default => throw new \Exception('Invalid resource type') + }; + $comment = new Comment(); $comment->parseComment($github->getComment($owner, $repositoryName, $commentId)); - $comment->addBuild($project, $resource, $status, $deployment->getId(), ['type' => 'logs']); + $comment->addBuild($project, $resource, $resourceType, $status, $deployment->getId(), ['type' => 'logs'], $previewUrl, $previweQrCode); $github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment()); } finally { $dbForConsole->deleteDocument('vcsCommentLocks', $commentId); diff --git a/src/Appwrite/Vcs/Comment.php b/src/Appwrite/Vcs/Comment.php index 18379f1099..e1d423d772 100644 --- a/src/Appwrite/Vcs/Comment.php +++ b/src/Appwrite/Vcs/Comment.php @@ -27,19 +27,22 @@ class Comment return \count($this->builds) === 0; } - public function addBuild(Document $project, Document $function, string $buildStatus, string $deploymentId, array $action): void + public function addBuild(Document $project, Document $resource, string $resourceType, string $buildStatus, string $deploymentId, array $action, string $previewUrl, string $previewQrCode): void { // Unique index - $id = $project->getId() . '_' . $function->getId(); + $id = $project->getId() . '_' . $resource->getId(); $this->builds[$id] = [ 'projectName' => $project->getAttribute('name'), 'projectId' => $project->getId(), - 'functionName' => $function->getAttribute('name'), - 'functionId' => $function->getId(), + 'resourceName' => $resource->getAttribute('name'), + 'resourceId' => $resource->getId(), + 'resourceType' => $resourceType, 'buildStatus' => $buildStatus, 'deploymentId' => $deploymentId, 'action' => $action, + 'previewQrCode' => $previewQrCode, + 'previewUrl' => $previewUrl, ]; } @@ -55,68 +58,108 @@ class Comment if (!\array_key_exists($build['projectId'], $projects)) { $projects[$build['projectId']] = [ 'name' => $build['projectName'], - 'functions' => [] + 'function' => [], + 'site' => [] ]; } - $projects[$build['projectId']]['functions'][$build['functionId']] = [ - 'name' => $build['functionName'], - 'status' => $build['buildStatus'], - 'deploymentId' => $build['deploymentId'], - 'action' => $build['action'], - ]; + if ($build['resourceType'] === 'site') { + $projects[$build['projectId']]['site'][$build['resourceId']] = [ + 'name' => $build['resourceName'], + 'status' => $build['buildStatus'], + 'deploymentId' => $build['deploymentId'], + 'action' => $build['action'], + 'previewUrl' => $build['previewUrl'], + 'previewQrCode' => $build['previewQrCode'] + ]; + } elseif ($build['resourceType'] === 'function') { + $projects[$build['projectId']]['function'][$build['resourceId']] = [ + 'name' => $build['resourceName'], + 'status' => $build['buildStatus'], + 'deploymentId' => $build['deploymentId'], + 'action' => $build['action'], + ]; + } } foreach ($projects as $projectId => $project) { - $text .= "**{$project['name']}** `{$projectId}`\n\n"; - $text .= "| Function | ID | Status | Action |\n"; - $text .= "| :- | :- | :- | :- |\n"; - $protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https'; $hostname = System::getEnv('_APP_DOMAIN'); - foreach ($project['functions'] as $functionId => $function) { - if ($function['status'] === 'waiting' || $function['status'] === 'processing' || $function['status'] === 'building') { - $text .= "**Your function deployment is in progress. Please check back in a few minutes for the updated status.**\n\n"; - } elseif ($function['status'] === 'ready') { - $text .= "**Your function has been successfully deployed.**\n\n"; - } else { - $text .= "**Your function deployment has failed. Please check the logs for more details and retry.**\n\n"; + $text .= "Project name: **{$project['name']}** \nProject ID: `{$projectId}`\n\n"; + + if (\count($project['site']) > 0) { + + $text .= "| Site | ID | Status | Previews | Action |\n"; + $text .= "| :- | :- | :- | :- | :- |\n"; + + foreach ($project['site'] as $siteId => $site) { + $generateImage = function (string $status) use ($protocol, $hostname) { + $extention = $status === 'building' ? 'gif' : 'png'; + $imagesUrl = $protocol . '://' . $hostname . '/console/images/vcs/'; + $imageUrl = '' . $status . ''; + + return $imageUrl; + }; + + $status = match ($site['status']) { + 'waiting' => $generateImage('waiting') . ' Waiting to build', + 'processing' => $generateImage('processing') . ' Processing', + 'building' => $generateImage('building') . ' Building', + 'ready' => $generateImage('ready') . ' Ready', + 'failed' => $generateImage('failed') . ' Failed', + }; + + if ($site['action']['type'] === 'logs') { + $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/sites/site-' . $siteId . '/deployment-' . $site['deploymentId'] . ')'; + } else { + $action = '[Authorize](' . $site['action']['url'] . ')'; + } + + $previews = '[Preview URL](' . $site['previewUrl'] . ') [QR Code](' . $site['previewQrCode'] . ')'; + + $text .= "| {$site['name']} | `{$siteId}` | {$status} | {$previews} | {$action} |\n"; } - $text .= "Project name: **{$project['name']}** \nProject ID: `{$projectId}`\n\n"; + $text .= "\n\n"; + } + + if (\count($project['function']) > 0) { + $text .= "| Function | ID | Status | Action |\n"; $text .= "| :- | :- | :- | :- |\n"; - $generateImage = function (string $status) use ($protocol, $hostname) { - $extention = $status === 'building' ? 'gif' : 'png'; - $imagesUrl = $protocol . '://' . $hostname . '/images/vcs/'; - $imageUrl = '' . $status . ''; + foreach ($project['function'] as $functionId => $function) { + $generateImage = function (string $status) use ($protocol, $hostname) { + $extention = $status === 'building' ? 'gif' : 'png'; + $imagesUrl = $protocol . '://' . $hostname . '/images/vcs/'; + $imageUrl = '' . $status . ''; + return $imageUrl; + }; - return $imageUrl; - }; + $status = match ($function['status']) { + 'waiting' => $generateImage('waiting') . ' Waiting to build', + 'processing' => $generateImage('processing') . ' Processing', + 'building' => $generateImage('building') . ' Building', + 'ready' => $generateImage('ready') . ' Ready', + 'failed' => $generateImage('failed') . ' Failed', + }; - $status = match ($function['status']) { - 'waiting' => $generateImage('waiting') . ' Waiting to build', - 'processing' => $generateImage('processing') . ' Processing', - 'building' => $generateImage('building') . ' Building', - 'ready' => $generateImage('ready') . ' Ready', - 'failed' => $generateImage('failed') . ' Failed', - }; + if ($function['action']['type'] === 'logs') { + $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')'; + } else { + $action = '[Authorize](' . $function['action']['url'] . ')'; + } - if ($function['action']['type'] === 'logs') { - $action = '[View Logs](' . $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId . '/deployment-' . $function['deploymentId'] . ')'; - } else { - $action = '[Authorize](' . $function['action']['url'] . ')'; + $text .= "| {$function['name']} | `{$functionId}` | {$status} | {$action} |\n"; } - $text .= "| {$function['name']} | `{$functionId}` | {$status} | {$action} |\n"; + $text .= "\n\n"; } - $text .= "\n\n"; } - $functionUrl = $protocol . '://' . $hostname . '/console/project-' . $projectId . '/functions/function-' . $functionId; - $text .= "Only deployments on the production branch are activated automatically. If you'd like to activate this deployment, navigate to [your deployments]($functionUrl). Learn more about Appwrite [Function deployments](https://appwrite.io/docs/functions).\n\n"; + + $text .= "Only deployments on the production branch are activated automatically. Learn more about Appwrite [Functions](https://appwrite.io/docs/functions) and [Sites](https://appwrite.io/docs/sites).\n\n"; $tip = $this->tips[array_rand($this->tips)]; $text .= "> **💡 Did you know?** \n " . $tip . "\n\n";