From 47fddbe8e5a39efef70c8b674ee8e5cf65080140 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sun, 10 Mar 2024 21:33:57 +0100 Subject: [PATCH 01/12] fix(vcs): fix deployments stuck at processing Ensure project is passed in the event because the init hook isn't able to detect the project. Make sure the build is triggered for each function. Reset the build event so the shutdown hook doesn't trigger again. --- app/controllers/api/vcs.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index ea30d6b11d..761fb4b350 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -238,11 +238,16 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) - ->setDeployment($deployment); + ->setDeployment($deployment) + ->setProject($project); // set the project because it won't be set for git deployments + + $queueForBuilds->trigger(); // must trigger here so that we create a build for each function //TODO: Add event? } } + + $queueForBuilds->setType(''); // prevent shutdown hook from triggering again }; App::get('/v1/vcs/github/authorize') From 9d6595f85d001bd6bc5771e56024a7e720ec946e Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sun, 10 Mar 2024 21:43:22 +0100 Subject: [PATCH 02/12] fix(vcs): prevent an error with one function deployment stopping others --- app/controllers/api/vcs.php | 355 ++++++++++++++++++------------------ 1 file changed, 182 insertions(+), 173 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 761fb4b350..5ad389fca4 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -41,213 +41,222 @@ use Utopia\VCS\Exception\RepositoryNotFound; use function Swoole\Coroutine\batch; $createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForConsole, Build $queueForBuilds, callable $getProjectDB, Request $request) { + $errors = []; foreach ($repositories as $resource) { - $resourceType = $resource->getAttribute('resourceType'); + try { + $resourceType = $resource->getAttribute('resourceType'); - if ($resourceType === "function") { - $projectId = $resource->getAttribute('projectId'); - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - $dbForProject = $getProjectDB($project); + if ($resourceType === "function") { + $projectId = $resource->getAttribute('projectId'); + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $dbForProject = $getProjectDB($project); - $functionId = $resource->getAttribute('resourceId'); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $functionInternalId = $function->getInternalId(); + $functionId = $resource->getAttribute('resourceId'); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $functionInternalId = $function->getInternalId(); - $deploymentId = ID::unique(); - $repositoryId = $resource->getId(); - $repositoryInternalId = $resource->getInternalId(); - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - $installationId = $resource->getAttribute('installationId'); - $installationInternalId = $resource->getAttribute('installationInternalId'); - $productionBranch = $function->getAttribute('providerBranch'); - $activate = false; + $deploymentId = ID::unique(); + $repositoryId = $resource->getId(); + $repositoryInternalId = $resource->getInternalId(); + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); + $installationId = $resource->getAttribute('installationId'); + $installationInternalId = $resource->getAttribute('installationInternalId'); + $productionBranch = $function->getAttribute('providerBranch'); + $activate = false; - if ($providerBranch == $productionBranch && $external === false) { - $activate = true; - } + if ($providerBranch == $productionBranch && $external === false) { + $activate = true; + } + + $owner = $github->getOwnerName($providerInstallationId) ?? ''; + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } - $owner = $github->getOwnerName($providerInstallationId) ?? ''; - try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; if (empty($repositoryName)) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } - } catch (RepositoryNotFound $e) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } + $isAuthorized = !$external; - $isAuthorized = !$external; - - if (!$isAuthorized && !empty($providerPullRequestId)) { - if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) { - $isAuthorized = true; - } - } - - $commentStatus = $isAuthorized ? 'waiting' : 'failed'; - - $authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}"; - - $action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl]; - - $latestCommentId = ''; - - if (!empty($providerPullRequestId)) { - $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ - Query::equal('providerRepositoryId', [$providerRepositoryId]), - Query::equal('providerPullRequestId', [$providerPullRequestId]), - Query::orderDesc('$createdAt'), - ])); - - if ($latestComment !== false && !$latestComment->isEmpty()) { - $latestCommentId = $latestComment->getAttribute('providerCommentId', ''); - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); - - $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); - } else { - $comment = new Comment(); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); - $latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment())); - - if (!empty($latestCommentId)) { - $teamId = $project->getAttribute('teamId', ''); - - $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::team(ID::custom($teamId))), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - ], - 'installationInternalId' => $installationInternalId, - 'installationId' => $installationId, - 'projectInternalId' => $project->getInternalId(), - 'projectId' => $project->getId(), - 'providerRepositoryId' => $providerRepositoryId, - 'providerBranch' => $providerBranch, - 'providerPullRequestId' => $providerPullRequestId, - 'providerCommentId' => $latestCommentId - ]))); + if (!$isAuthorized && !empty($providerPullRequestId)) { + if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) { + $isAuthorized = true; } } - } elseif (!empty($providerBranch)) { - $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ - Query::equal('providerRepositoryId', [$providerRepositoryId]), - Query::equal('providerBranch', [$providerBranch]), - Query::orderDesc('$createdAt'), - ])); - foreach ($latestComments as $comment) { - $latestCommentId = $comment->getAttribute('providerCommentId', ''); - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + $commentStatus = $isAuthorized ? 'waiting' : 'failed'; - $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + $authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}"; + + $action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl]; + + $latestCommentId = ''; + + if (!empty($providerPullRequestId)) { + $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ + Query::equal('providerRepositoryId', [$providerRepositoryId]), + Query::equal('providerPullRequestId', [$providerPullRequestId]), + Query::orderDesc('$createdAt'), + ])); + + if ($latestComment !== false && !$latestComment->isEmpty()) { + $latestCommentId = $latestComment->getAttribute('providerCommentId', ''); + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + + $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + } else { + $comment = new Comment(); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + $latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment())); + + if (!empty($latestCommentId)) { + $teamId = $project->getAttribute('teamId', ''); + + $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::team(ID::custom($teamId))), + Permission::update(Role::team(ID::custom($teamId), 'owner')), + Permission::update(Role::team(ID::custom($teamId), 'developer')), + Permission::delete(Role::team(ID::custom($teamId), 'owner')), + Permission::delete(Role::team(ID::custom($teamId), 'developer')), + ], + 'installationInternalId' => $installationInternalId, + 'installationId' => $installationId, + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'providerRepositoryId' => $providerRepositoryId, + 'providerBranch' => $providerBranch, + 'providerPullRequestId' => $providerPullRequestId, + 'providerCommentId' => $latestCommentId + ]))); + } + } + } elseif (!empty($providerBranch)) { + $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ + Query::equal('providerRepositoryId', [$providerRepositoryId]), + Query::equal('providerBranch', [$providerBranch]), + Query::orderDesc('$createdAt'), + ])); + + foreach ($latestComments as $comment) { + $latestCommentId = $comment->getAttribute('providerCommentId', ''); + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + + $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + } } - } - if (!$isAuthorized) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Authorization required for external contributor.'; + if (!$isAuthorized) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Authorization required for external contributor.'; - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } - } catch (RepositoryNotFound $e) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + $owner = $github->getOwnerName($providerInstallationId); + $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'failure', $message, $authorizeUrl, $name); + continue; } - $owner = $github->getOwnerName($providerInstallationId); - $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'failure', $message, $authorizeUrl, $name); - continue; - } - if ($external) { - $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $providerPullRequestId); - $providerRepositoryName = $pullRequestResponse['head']['repo']['owner']['login']; - $providerRepositoryOwner = $pullRequestResponse['head']['repo']['name']; - } + if ($external) { + $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $providerPullRequestId); + $providerRepositoryName = $pullRequestResponse['head']['repo']['owner']['login']; + $providerRepositoryOwner = $pullRequestResponse['head']['repo']['name']; + } - $deployment = $dbForProject->createDocument('deployments', new Document([ - '$id' => $deploymentId, - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'resourceId' => $functionId, - 'resourceInternalId' => $functionInternalId, - 'resourceType' => 'functions', - 'entrypoint' => $function->getAttribute('entrypoint'), - 'commands' => $function->getAttribute('commands'), - 'type' => 'vcs', - 'installationId' => $installationId, - 'installationInternalId' => $installationInternalId, - 'providerRepositoryId' => $providerRepositoryId, - 'repositoryId' => $repositoryId, - 'repositoryInternalId' => $repositoryInternalId, - 'providerBranchUrl' => $providerBranchUrl, - 'providerRepositoryName' => $providerRepositoryName, - 'providerRepositoryOwner' => $providerRepositoryOwner, - 'providerRepositoryUrl' => $providerRepositoryUrl, - 'providerCommitHash' => $providerCommitHash, - 'providerCommitAuthorUrl' => $providerCommitAuthorUrl, - 'providerCommitAuthor' => $providerCommitAuthor, - 'providerCommitMessage' => $providerCommitMessage, - 'providerCommitUrl' => $providerCommitUrl, - 'providerCommentId' => \strval($latestCommentId), - 'providerBranch' => $providerBranch, - 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), - 'activate' => $activate, - ])); + $deployment = $dbForProject->createDocument('deployments', new Document([ + '$id' => $deploymentId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'resourceId' => $functionId, + 'resourceInternalId' => $functionInternalId, + 'resourceType' => 'functions', + 'entrypoint' => $function->getAttribute('entrypoint'), + 'commands' => $function->getAttribute('commands'), + 'type' => 'vcs', + 'installationId' => $installationId, + 'installationInternalId' => $installationInternalId, + 'providerRepositoryId' => $providerRepositoryId, + 'repositoryId' => $repositoryId, + 'repositoryInternalId' => $repositoryInternalId, + 'providerBranchUrl' => $providerBranchUrl, + 'providerRepositoryName' => $providerRepositoryName, + 'providerRepositoryOwner' => $providerRepositoryOwner, + 'providerRepositoryUrl' => $providerRepositoryUrl, + 'providerCommitHash' => $providerCommitHash, + 'providerCommitAuthorUrl' => $providerCommitAuthorUrl, + 'providerCommitAuthor' => $providerCommitAuthor, + 'providerCommitMessage' => $providerCommitMessage, + 'providerCommitUrl' => $providerCommitUrl, + 'providerCommentId' => \strval($latestCommentId), + 'providerBranch' => $providerBranch, + 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), + 'activate' => $activate, + ])); - if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Starting...'; + if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Starting...'; - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } - } catch (RepositoryNotFound $e) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + $owner = $github->getOwnerName($providerInstallationId); + + $providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; + $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name); } - $owner = $github->getOwnerName($providerInstallationId); - $providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; - $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name); + $queueForBuilds + ->setType(BUILD_TYPE_DEPLOYMENT) + ->setResource($function) + ->setDeployment($deployment) + ->setProject($project); // set the project because it won't be set for git deployments + + $queueForBuilds->trigger(); // must trigger here so that we create a build for each function + + //TODO: Add event? } - - $queueForBuilds - ->setType(BUILD_TYPE_DEPLOYMENT) - ->setResource($function) - ->setDeployment($deployment) - ->setProject($project); // set the project because it won't be set for git deployments - - $queueForBuilds->trigger(); // must trigger here so that we create a build for each function - - //TODO: Add event? + } catch (Throwable $e) { + $errors[] = $e->getMessage(); } } $queueForBuilds->setType(''); // prevent shutdown hook from triggering again + + if (!empty($errors)) { + throw new Exception(Exception::GENERAL_UNKNOWN, \implode("\n", $errors)); + } }; App::get('/v1/vcs/github/authorize') From 8e348dbd9288e1cbcc31068d84057c8d89c37387 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sun, 10 Mar 2024 21:53:57 +0100 Subject: [PATCH 03/12] refactor(vcs): reduce nested code --- app/controllers/api/vcs.php | 368 ++++++++++++++++++------------------ 1 file changed, 185 insertions(+), 183 deletions(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 5ad389fca4..bc62e87aa9 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -46,29 +46,127 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId try { $resourceType = $resource->getAttribute('resourceType'); - if ($resourceType === "function") { - $projectId = $resource->getAttribute('projectId'); - $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); - $dbForProject = $getProjectDB($project); + if ($resourceType !== "function") { + continue; + } - $functionId = $resource->getAttribute('resourceId'); - $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); - $functionInternalId = $function->getInternalId(); + $projectId = $resource->getAttribute('projectId'); + $project = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $projectId)); + $dbForProject = $getProjectDB($project); - $deploymentId = ID::unique(); - $repositoryId = $resource->getId(); - $repositoryInternalId = $resource->getInternalId(); - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - $installationId = $resource->getAttribute('installationId'); - $installationInternalId = $resource->getAttribute('installationInternalId'); - $productionBranch = $function->getAttribute('providerBranch'); - $activate = false; + $functionId = $resource->getAttribute('resourceId'); + $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); + $functionInternalId = $function->getInternalId(); - if ($providerBranch == $productionBranch && $external === false) { - $activate = true; + $deploymentId = ID::unique(); + $repositoryId = $resource->getId(); + $repositoryInternalId = $resource->getInternalId(); + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); + $installationId = $resource->getAttribute('installationId'); + $installationInternalId = $resource->getAttribute('installationInternalId'); + $productionBranch = $function->getAttribute('providerBranch'); + $activate = false; + + if ($providerBranch == $productionBranch && $external === false) { + $activate = true; + } + + $owner = $github->getOwnerName($providerInstallationId) ?? ''; + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } + } catch (RepositoryNotFound $e) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } - $owner = $github->getOwnerName($providerInstallationId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + + $isAuthorized = !$external; + + if (!$isAuthorized && !empty($providerPullRequestId)) { + if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) { + $isAuthorized = true; + } + } + + $commentStatus = $isAuthorized ? 'waiting' : 'failed'; + + $authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}"; + + $action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl]; + + $latestCommentId = ''; + + if (!empty($providerPullRequestId)) { + $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ + Query::equal('providerRepositoryId', [$providerRepositoryId]), + Query::equal('providerPullRequestId', [$providerPullRequestId]), + Query::orderDesc('$createdAt'), + ])); + + if ($latestComment !== false && !$latestComment->isEmpty()) { + $latestCommentId = $latestComment->getAttribute('providerCommentId', ''); + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + + $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + } else { + $comment = new Comment(); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + $latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment())); + + if (!empty($latestCommentId)) { + $teamId = $project->getAttribute('teamId', ''); + + $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ + '$id' => ID::unique(), + '$permissions' => [ + Permission::read(Role::team(ID::custom($teamId))), + Permission::update(Role::team(ID::custom($teamId), 'owner')), + Permission::update(Role::team(ID::custom($teamId), 'developer')), + Permission::delete(Role::team(ID::custom($teamId), 'owner')), + Permission::delete(Role::team(ID::custom($teamId), 'developer')), + ], + 'installationInternalId' => $installationInternalId, + 'installationId' => $installationId, + 'projectInternalId' => $project->getInternalId(), + 'projectId' => $project->getId(), + 'providerRepositoryId' => $providerRepositoryId, + 'providerBranch' => $providerBranch, + 'providerPullRequestId' => $providerPullRequestId, + 'providerCommentId' => $latestCommentId + ]))); + } + } + } elseif (!empty($providerBranch)) { + $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ + Query::equal('providerRepositoryId', [$providerRepositoryId]), + Query::equal('providerBranch', [$providerBranch]), + Query::orderDesc('$createdAt'), + ])); + + foreach ($latestComments as $comment) { + $latestCommentId = $comment->getAttribute('providerCommentId', ''); + $comment = new Comment(); + $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); + $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); + + $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); + } + } + + if (!$isAuthorized) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Authorization required for external contributor.'; + + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); try { $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; if (empty($repositoryName)) { @@ -77,176 +175,80 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } + $owner = $github->getOwnerName($providerInstallationId); + $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'failure', $message, $authorizeUrl, $name); + continue; + } - if (empty($repositoryName)) { + if ($external) { + $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $providerPullRequestId); + $providerRepositoryName = $pullRequestResponse['head']['repo']['owner']['login']; + $providerRepositoryOwner = $pullRequestResponse['head']['repo']['name']; + } + + $deployment = $dbForProject->createDocument('deployments', new Document([ + '$id' => $deploymentId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'resourceId' => $functionId, + 'resourceInternalId' => $functionInternalId, + 'resourceType' => 'functions', + 'entrypoint' => $function->getAttribute('entrypoint'), + 'commands' => $function->getAttribute('commands'), + 'type' => 'vcs', + 'installationId' => $installationId, + 'installationInternalId' => $installationInternalId, + 'providerRepositoryId' => $providerRepositoryId, + 'repositoryId' => $repositoryId, + 'repositoryInternalId' => $repositoryInternalId, + 'providerBranchUrl' => $providerBranchUrl, + 'providerRepositoryName' => $providerRepositoryName, + 'providerRepositoryOwner' => $providerRepositoryOwner, + 'providerRepositoryUrl' => $providerRepositoryUrl, + 'providerCommitHash' => $providerCommitHash, + 'providerCommitAuthorUrl' => $providerCommitAuthorUrl, + 'providerCommitAuthor' => $providerCommitAuthor, + 'providerCommitMessage' => $providerCommitMessage, + 'providerCommitUrl' => $providerCommitUrl, + 'providerCommentId' => \strval($latestCommentId), + 'providerBranch' => $providerBranch, + 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), + 'activate' => $activate, + ])); + + if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) { + $functionName = $function->getAttribute('name'); + $projectName = $project->getAttribute('name'); + $name = "{$functionName} ({$projectName})"; + $message = 'Starting...'; + + $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); + try { + $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; + if (empty($repositoryName)) { + throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); + } + } catch (RepositoryNotFound $e) { throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); } + $owner = $github->getOwnerName($providerInstallationId); - $isAuthorized = !$external; - - if (!$isAuthorized && !empty($providerPullRequestId)) { - if (\in_array($providerPullRequestId, $resource->getAttribute('providerPullRequestIds', []))) { - $isAuthorized = true; - } - } - - $commentStatus = $isAuthorized ? 'waiting' : 'failed'; - - $authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}"; - - $action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl]; - - $latestCommentId = ''; - - if (!empty($providerPullRequestId)) { - $latestComment = Authorization::skip(fn () => $dbForConsole->findOne('vcsComments', [ - Query::equal('providerRepositoryId', [$providerRepositoryId]), - Query::equal('providerPullRequestId', [$providerPullRequestId]), - Query::orderDesc('$createdAt'), - ])); - - if ($latestComment !== false && !$latestComment->isEmpty()) { - $latestCommentId = $latestComment->getAttribute('providerCommentId', ''); - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); - - $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); - } else { - $comment = new Comment(); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); - $latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment())); - - if (!empty($latestCommentId)) { - $teamId = $project->getAttribute('teamId', ''); - - $latestComment = Authorization::skip(fn () => $dbForConsole->createDocument('vcsComments', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::team(ID::custom($teamId))), - Permission::update(Role::team(ID::custom($teamId), 'owner')), - Permission::update(Role::team(ID::custom($teamId), 'developer')), - Permission::delete(Role::team(ID::custom($teamId), 'owner')), - Permission::delete(Role::team(ID::custom($teamId), 'developer')), - ], - 'installationInternalId' => $installationInternalId, - 'installationId' => $installationId, - 'projectInternalId' => $project->getInternalId(), - 'projectId' => $project->getId(), - 'providerRepositoryId' => $providerRepositoryId, - 'providerBranch' => $providerBranch, - 'providerPullRequestId' => $providerPullRequestId, - 'providerCommentId' => $latestCommentId - ]))); - } - } - } elseif (!empty($providerBranch)) { - $latestComments = Authorization::skip(fn () => $dbForConsole->find('vcsComments', [ - Query::equal('providerRepositoryId', [$providerRepositoryId]), - Query::equal('providerBranch', [$providerBranch]), - Query::orderDesc('$createdAt'), - ])); - - foreach ($latestComments as $comment) { - $latestCommentId = $comment->getAttribute('providerCommentId', ''); - $comment = new Comment(); - $comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId)); - $comment->addBuild($project, $function, $commentStatus, $deploymentId, $action); - - $latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment())); - } - } - - if (!$isAuthorized) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Authorization required for external contributor.'; - - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - } catch (RepositoryNotFound $e) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - $owner = $github->getOwnerName($providerInstallationId); - $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'failure', $message, $authorizeUrl, $name); - continue; - } - - if ($external) { - $pullRequestResponse = $github->getPullRequest($owner, $repositoryName, $providerPullRequestId); - $providerRepositoryName = $pullRequestResponse['head']['repo']['owner']['login']; - $providerRepositoryOwner = $pullRequestResponse['head']['repo']['name']; - } - - $deployment = $dbForProject->createDocument('deployments', new Document([ - '$id' => $deploymentId, - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'resourceId' => $functionId, - 'resourceInternalId' => $functionInternalId, - 'resourceType' => 'functions', - 'entrypoint' => $function->getAttribute('entrypoint'), - 'commands' => $function->getAttribute('commands'), - 'type' => 'vcs', - 'installationId' => $installationId, - 'installationInternalId' => $installationInternalId, - 'providerRepositoryId' => $providerRepositoryId, - 'repositoryId' => $repositoryId, - 'repositoryInternalId' => $repositoryInternalId, - 'providerBranchUrl' => $providerBranchUrl, - 'providerRepositoryName' => $providerRepositoryName, - 'providerRepositoryOwner' => $providerRepositoryOwner, - 'providerRepositoryUrl' => $providerRepositoryUrl, - 'providerCommitHash' => $providerCommitHash, - 'providerCommitAuthorUrl' => $providerCommitAuthorUrl, - 'providerCommitAuthor' => $providerCommitAuthor, - 'providerCommitMessage' => $providerCommitMessage, - 'providerCommitUrl' => $providerCommitUrl, - 'providerCommentId' => \strval($latestCommentId), - 'providerBranch' => $providerBranch, - 'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]), - 'activate' => $activate, - ])); - - if (!empty($providerCommitHash) && $function->getAttribute('providerSilentMode', false) === false) { - $functionName = $function->getAttribute('name'); - $projectName = $project->getAttribute('name'); - $name = "{$functionName} ({$projectName})"; - $message = 'Starting...'; - - $providerRepositoryId = $resource->getAttribute('providerRepositoryId'); - try { - $repositoryName = $github->getRepositoryName($providerRepositoryId) ?? ''; - if (empty($repositoryName)) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - } catch (RepositoryNotFound $e) { - throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND); - } - $owner = $github->getOwnerName($providerInstallationId); - - $providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; - $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name); - } - - $queueForBuilds - ->setType(BUILD_TYPE_DEPLOYMENT) - ->setResource($function) - ->setDeployment($deployment) - ->setProject($project); // set the project because it won't be set for git deployments - - $queueForBuilds->trigger(); // must trigger here so that we create a build for each function - - //TODO: Add event? + $providerTargetUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/project-$projectId/functions/function-$functionId"; + $github->updateCommitStatus($repositoryName, $providerCommitHash, $owner, 'pending', $message, $providerTargetUrl, $name); } + + $queueForBuilds + ->setType(BUILD_TYPE_DEPLOYMENT) + ->setResource($function) + ->setDeployment($deployment) + ->setProject($project); // set the project because it won't be set for git deployments + + $queueForBuilds->trigger(); // must trigger here so that we create a build for each function + + //TODO: Add event? } catch (Throwable $e) { $errors[] = $e->getMessage(); } From b7be370a5372b11f8c290a44ffb54949202ff9d3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sun, 10 Mar 2024 23:13:55 +0100 Subject: [PATCH 04/12] fix(builds): use standard Exception --- src/Appwrite/Platform/Workers/Builds.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index b9f02a8f67..31bf961c30 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -73,7 +73,7 @@ class Builds extends Action $payload = $message->getPayload() ?? []; if (empty($payload)) { - throw new Exception('Missing payload'); + throw new \Exception('Missing payload'); } $type = $payload['type'] ?? ''; @@ -124,7 +124,7 @@ class Builds extends Action $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404); + throw new \Exception('Function not found', 404); } $deploymentId = $deployment->getId(); @@ -132,11 +132,11 @@ class Builds extends Action $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404); + throw new \Exception('Deployment not found', 404); } if (empty($deployment->getAttribute('entrypoint', ''))) { - throw new Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); + throw new \Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); } $version = $function->getAttribute('version', 'v2'); @@ -144,7 +144,7 @@ class Builds extends Action $key = $function->getAttribute('runtime'); $runtime = $runtimes[$key] ?? null; if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); + throw new \Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } // Realtime preparation @@ -306,7 +306,7 @@ class Builds extends Action $directorySize = $localDevice->getDirectorySize($tmpDirectory); $functionsSizeLimit = (int) App::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000'); if ($directorySize > $functionsSizeLimit) { - throw new Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); + throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.'); } Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); @@ -431,7 +431,7 @@ class Builds extends Action $build = $dbForProject->getDocument('builds', $build->getId()); if ($build->isEmpty()) { - throw new Exception('Build not found', 404); + throw new \Exception('Build not found', 404); } $build = $build->setAttribute('logs', $build->getAttribute('logs', '') . $logs); From b9b891a90b49a39ed39294dba225b39afbd6f2c0 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Sun, 10 Mar 2024 23:16:29 +0100 Subject: [PATCH 05/12] fix(builds): fix float to int warning Implicit conversion of float to int is deprecated and emits: Deprecated: Implicit conversion from float N to int loses precision Use floor to truncate. --- src/Appwrite/Platform/Workers/Builds.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 31bf961c30..42cd910a1f 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -472,7 +472,7 @@ class Builds extends Action $durationEnd = \microtime(true); /** Update the build document */ - $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp($response['startTime']))); + $build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime'])))); $build->setAttribute('endTime', $endTime); $build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart))); $build->setAttribute('status', 'ready'); From b19efb619b9ea5bd911838bc21bd7840127e9fb3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 11:17:40 +0100 Subject: [PATCH 06/12] feat(events): update build event reset to reset everything Instead of using setType('') to prevent events from triggering, it makes more sense to use reset(). However, reset() didn't properly reset type, resource, deployment, or template. This change makes sure to reset all those private variables. --- app/controllers/api/vcs.php | 2 +- src/Appwrite/Event/Build.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index bc62e87aa9..c041155ef7 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -254,7 +254,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } - $queueForBuilds->setType(''); // prevent shutdown hook from triggering again + $queueForBuilds->reset(); // prevent shutdown hook from triggering again if (!empty($errors)) { throw new Exception(Exception::GENERAL_UNKNOWN, \implode("\n", $errors)); diff --git a/src/Appwrite/Event/Build.php b/src/Appwrite/Event/Build.php index 496db87d64..b8cb62a6f8 100644 --- a/src/Appwrite/Event/Build.php +++ b/src/Appwrite/Event/Build.php @@ -122,4 +122,20 @@ class Build extends Event 'template' => $this->template ]); } + + /** + * Resets event. + * + * @return self + */ + public function reset(): self + { + $this->type = ''; + $this->resource = null; + $this->deployment = null; + $this->template = null; + parent::reset(); + + return $this; + } } From c9fdfb0f4be52b07df8f55babdf46978c2ea2e2d Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 15:44:36 +0000 Subject: [PATCH 07/12] fix(auth): allow wildcards for url validation like oauth success --- composer.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.lock b/composer.lock index 046fc70983..0358bdc53f 100644 --- a/composer.lock +++ b/composer.lock @@ -1719,16 +1719,16 @@ }, { "name": "utopia-php/framework", - "version": "0.33.2", + "version": "0.33.3", "source": { "type": "git", "url": "https://github.com/utopia-php/http.git", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3" + "reference": "893d602cd96676810c25fc9b9a2e9360eebb898f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/http/zipball/b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", - "reference": "b1423ca3e3b61c6c4c2e619d2cb80672809a19f3", + "url": "https://api.github.com/repos/utopia-php/http/zipball/893d602cd96676810c25fc9b9a2e9360eebb898f", + "reference": "893d602cd96676810c25fc9b9a2e9360eebb898f", "shasum": "" }, "require": { @@ -1758,9 +1758,9 @@ ], "support": { "issues": "https://github.com/utopia-php/http/issues", - "source": "https://github.com/utopia-php/http/tree/0.33.2" + "source": "https://github.com/utopia-php/http/tree/0.33.3" }, - "time": "2024-01-31T10:35:59+00:00" + "time": "2024-03-11T13:43:23+00:00" }, { "name": "utopia-php/image", From 5bbfffff8667144a695a18063793a494805324b5 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 17:13:50 +0100 Subject: [PATCH 08/12] Bump console to version 4.0.4 --- .gitmodules | 2 +- app/console | 2 +- app/init.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index b9e6871458..f944bb1705 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 4.0.3 + branch = 4.0.4 diff --git a/app/console b/app/console index afb50f720d..d75ef00fb0 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit afb50f720d94d0d92a4807d1931624fc55b41386 +Subproject commit d75ef00fb088c909bf8fdc5b12c2fe25ed270b43 diff --git a/app/init.php b/app/init.php index b382bfb62c..710b7053b1 100644 --- a/app/init.php +++ b/app/init.php @@ -111,7 +111,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 332; +const APP_CACHE_BUSTER = 404; const APP_VERSION_STABLE = '1.5.2'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; From d167fb3650f1249da1fafb9e70da77df4364c20c Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 17:59:12 +0100 Subject: [PATCH 09/12] Bump appwrite version to 1.5.3 --- README-CN.md | 6 +++--- README.md | 6 +++--- app/init.php | 2 +- src/Appwrite/Migration/Migration.php | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README-CN.md b/README-CN.md index 27af8b8b7f..8942c2220e 100644 --- a/README-CN.md +++ b/README-CN.md @@ -66,7 +66,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` ### Windows @@ -78,7 +78,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` #### PowerShell @@ -88,7 +88,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index 86cc512460..ea37336d62 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` ### Windows @@ -88,7 +88,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` #### PowerShell @@ -98,7 +98,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.2 + appwrite/appwrite:1.5.3 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/init.php b/app/init.php index 710b7053b1..e954689d54 100644 --- a/app/init.php +++ b/app/init.php @@ -112,7 +112,7 @@ const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours const APP_CACHE_BUSTER = 404; -const APP_VERSION_STABLE = '1.5.2'; +const APP_VERSION_STABLE = '1.5.3'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 7a8a0040ae..c0731d300b 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -81,6 +81,7 @@ abstract class Migration '1.5.0' => 'V20', '1.5.1' => 'V20', '1.5.2' => 'V20', + '1.5.3' => 'V20', ]; /** From cbd7d9c56804e3d7e83292bb205cdf54d6a70ec3 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Mon, 11 Mar 2024 18:07:20 +0100 Subject: [PATCH 10/12] Add 1.5.3 to CHANGES.md --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1aa7035b11..08c04e32dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +# Version 1.5.3 + +## What's Changed + +### Fixes + +* Fix Attribute not found when migrating users collection in [#7782](https://github.com/appwrite/appwrite/pull/7782) +* Fix git deployments in [#7780](https://github.com/appwrite/appwrite/pull/7780) +* Allow wildcards for url validation like OAuth2 success in [#7791](https://github.com/appwrite/appwrite/pull/7791) + # Version 1.5.2 ## What's Changed * Fix stats migration by @abnegate in https://github.com/appwrite/appwrite/pull/7760 From 7273d0e1177c70ee45fe8049459e70ef574515a6 Mon Sep 17 00:00:00 2001 From: Steven Nguyen Date: Wed, 13 Mar 2024 01:03:51 +0100 Subject: [PATCH 11/12] Escape function build command The extra trim is neded to because the extra quotes interfere with additional operations in the executor/orchestration library. --- src/Appwrite/Platform/Workers/Builds.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index 42cd910a1f..8a25980bc1 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -394,7 +394,6 @@ class Builds extends Action ]); $command = $deployment->getAttribute('commands', ''); - $command = \str_replace('"', '\\"', $command); $response = null; $err = null; @@ -403,7 +402,7 @@ class Builds extends Action Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) { try { $version = $function->getAttribute('version', 'v2'); - $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . $command . '"'; + $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"'; $response = $executor->createRuntime( deploymentId: $deployment->getId(), From c6ce585fcc87f7049d94e2cc40e2b7a5927998e0 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 13 Mar 2024 11:08:50 +0100 Subject: [PATCH 12/12] Update version --- CHANGES.md | 8 ++++++-- README-CN.md | 6 +++--- README.md | 6 +++--- app/init.php | 4 ++-- src/Appwrite/Migration/Migration.php | 1 + 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 08c04e32dc..ced1b92f55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,7 +1,11 @@ -# Version 1.5.3 - +# Version 1.5.4 ## What's Changed +### Fixes +* Fix function build command by @abnegate in https://github.com/appwrite/appwrite/pull/7813 + +# Version 1.5.3 +## What's Changed ### Fixes * Fix Attribute not found when migrating users collection in [#7782](https://github.com/appwrite/appwrite/pull/7782) diff --git a/README-CN.md b/README-CN.md index 8942c2220e..59dad38cd2 100644 --- a/README-CN.md +++ b/README-CN.md @@ -66,7 +66,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` ### Windows @@ -78,7 +78,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` #### PowerShell @@ -88,7 +88,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` 运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。 diff --git a/README.md b/README.md index ea37336d62..792a5482ef 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ docker run -it --rm \ --volume /var/run/docker.sock:/var/run/docker.sock \ --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \ --entrypoint="install" \ - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` ### Windows @@ -88,7 +88,7 @@ docker run -it --rm ^ --volume //var/run/docker.sock:/var/run/docker.sock ^ --volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^ --entrypoint="install" ^ - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` #### PowerShell @@ -98,7 +98,7 @@ docker run -it --rm ` --volume /var/run/docker.sock:/var/run/docker.sock ` --volume ${pwd}/appwrite:/usr/src/code/appwrite:rw ` --entrypoint="install" ` - appwrite/appwrite:1.5.3 + appwrite/appwrite:1.5.4 ``` Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation. diff --git a/app/init.php b/app/init.php index e954689d54..4b70b2601b 100644 --- a/app/init.php +++ b/app/init.php @@ -111,8 +111,8 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 404; -const APP_VERSION_STABLE = '1.5.3'; +const APP_CACHE_BUSTER = 405; +const APP_VERSION_STABLE = '1.5.4'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; const APP_DATABASE_ATTRIBUTE_IP = 'ip'; diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index c0731d300b..76cbbebe03 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -82,6 +82,7 @@ abstract class Migration '1.5.1' => 'V20', '1.5.2' => 'V20', '1.5.3' => 'V20', + '1.5.4' => 'V20', ]; /**