From d9522ac6b8bef52b1a6e1e56a103180a71df3edc Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 14 Nov 2025 20:44:48 +0530 Subject: [PATCH 1/8] Update deploymentId in sites rule for first deployment --- .../Platform/Modules/Compute/Base.php | 30 +++++++++++++++++++ .../Modules/Sites/Http/Deployments/Create.php | 5 +++- .../Http/Deployments/Template/Create.php | 2 ++ tests/e2e/Services/Sites/SitesBase.php | 23 ++++++++++++++ .../Services/Sites/SitesCustomServerTest.php | 14 +++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index a538eb1497..5fae52ff45 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -14,6 +14,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Validator\Authorization; +use Utopia\Database\Query; use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub; @@ -335,4 +336,33 @@ class Base extends Action return $deployment; } + + /** + * Update manual rule for new site deployment. + * In case of fresh site, deployment ID will be empty in the rules, so we need to update it here. + * + * @param \Utopia\Database\Document $project + * @param \Utopia\Database\Document $site + * @param \Utopia\Database\Document $deployment + * @param \Utopia\Database\Database $dbForPlatform + * @return void + */ + public function updateManualRuleForNewSiteDeployment(Document $project, Document $site, Document $deployment, Database $dbForPlatform) + { + $queries = [ + Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('type', ['deployment']), + Query::equal('deploymentResourceInternalId', [$site->getSequence()]), + Query::equal('deploymentResourceType', ['site']), + Query::equal('trigger', ['manual']), + ]; + $dbForPlatform->forEach('rules', function (Document $rule) use ($deployment, $dbForPlatform) { + if (empty($rule->getAttribute('deploymentId', ''))) { + Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ + 'deploymentId' => $deployment->getId(), + 'deploymentInternalId' => $deployment->getSequence(), + ]))); + } + }, $queries); + } } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 4bd3afa1f5..70546bf3f7 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Sites\Http\Deployments; use Appwrite\Event\Build; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -31,7 +32,7 @@ use Utopia\System\System; use Utopia\Validator\Boolean; use Utopia\Validator\Text; -class Create extends Action +class Create extends Base { use HTTP; @@ -364,6 +365,8 @@ class Create extends Action } } + $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $metadata = null; $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index dc90045b0c..2407becd68 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -196,6 +196,8 @@ class Create extends Base ])) ); + $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 93c55b82b7..3f82a42dca 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -450,4 +450,27 @@ trait SitesBase return $specifications; } + + protected function createRule(string $siteId, string $domain): mixed + { + $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'siteId' => $siteId, + 'domain' => $domain, + ]); + + return $rule; + } + + protected function getRule(string $ruleId): mixed + { + $rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $rule; + } } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 8591514796..df29231562 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -861,6 +861,9 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); + $rule = $this->createRule($siteId, \uniqid() . '.myapp.com'); + $this->assertEquals(201, $rule['headers']['status-code']); + $deployment = $this->createDeployment($siteId, [ 'siteId' => $siteId, 'code' => $this->packageSite('static-single-file'), @@ -872,6 +875,10 @@ class SitesCustomServerTest extends Scope $this->assertEquals('waiting', $deployment['body']['status']); $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); + $rule = $this->getRule($rule['body']['$id']); + $this->assertEquals(200, $rule['headers']['status-code']); + $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + $deploymentIdActive = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($siteId, $deploymentIdActive) { @@ -1563,6 +1570,9 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); + $rule = $this->createRule($siteId, \uniqid() . '.myapp.com'); + $this->assertEquals(201, $rule['headers']['status-code']); + $deployment = $this->createTemplateDeployment($siteId, [ 'repository' => $template['providerRepositoryId'], 'owner' => $template['providerOwner'], @@ -1574,6 +1584,10 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); + $rule = $this->getRule($rule['body']['$id']); + $this->assertEquals(200, $rule['headers']['status-code']); + $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); From d36e42377b859f7dbdd5dd32b1def240f43b4ee5 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Fri, 14 Nov 2025 20:49:06 +0530 Subject: [PATCH 2/8] include VCS --- src/Appwrite/Platform/Modules/Compute/Base.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 5fae52ff45..af06ca8000 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -328,6 +328,8 @@ class Base extends Action } } + $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($site) From ba542554ceacfbbe4ea7afe4648570ddd080f3e6 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 17 Nov 2025 14:53:26 +0530 Subject: [PATCH 3/8] functions and more tests --- .../Platform/Modules/Compute/Base.php | 28 ++++++++--------- .../Functions/Http/Deployments/Create.php | 7 ++++- .../Http/Deployments/Template/Create.php | 4 +++ .../Functions/Http/Deployments/Vcs/Create.php | 1 + .../Functions/Http/Functions/Create.php | 1 + .../Functions/Http/Functions/Update.php | 2 +- .../Modules/Sites/Http/Deployments/Create.php | 2 +- .../Http/Deployments/Template/Create.php | 2 +- .../Modules/Sites/Http/Sites/Update.php | 2 +- .../e2e/Services/Functions/FunctionsBase.php | 23 ++++++++++++++ .../Functions/FunctionsCustomServerTest.php | 31 +++++++++++++++++++ tests/e2e/Services/Sites/SitesBase.php | 4 +-- .../Services/Sites/SitesCustomServerTest.php | 30 ++++++++++++------ 13 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index af06ca8000..02650b9b85 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -13,8 +13,8 @@ use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\VCS\Adapter\Git\GitHub; @@ -53,7 +53,7 @@ class Base extends Action return $allowedSpecifications[0] ?? APP_COMPUTE_SPECIFICATION_DEFAULT; } - public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document + public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document { $deploymentId = ID::unique(); $entrypoint = $function->getAttribute('entrypoint', ''); @@ -134,6 +134,8 @@ class Base extends Action ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); + $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) @@ -328,7 +330,7 @@ class Base extends Action } } - $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) @@ -340,8 +342,8 @@ class Base extends Action } /** - * Update manual rule for new site deployment. - * In case of fresh site, deployment ID will be empty in the rules, so we need to update it here. + * Update empty manual rule for deployment. + * In case of first deployment, deployment ID will be empty in the rules, so we need to update it here. * * @param \Utopia\Database\Document $project * @param \Utopia\Database\Document $site @@ -349,22 +351,20 @@ class Base extends Action * @param \Utopia\Database\Database $dbForPlatform * @return void */ - public function updateManualRuleForNewSiteDeployment(Document $project, Document $site, Document $deployment, Database $dbForPlatform) + public function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform) { $queries = [ Query::equal('projectInternalId', [$project->getSequence()]), + Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), + Query::equal('deploymentId', ['']), Query::equal('type', ['deployment']), - Query::equal('deploymentResourceInternalId', [$site->getSequence()]), - Query::equal('deploymentResourceType', ['site']), Query::equal('trigger', ['manual']), ]; $dbForPlatform->forEach('rules', function (Document $rule) use ($deployment, $dbForPlatform) { - if (empty($rule->getAttribute('deploymentId', ''))) { - Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ - 'deploymentId' => $deployment->getId(), - 'deploymentInternalId' => $deployment->getSequence(), - ]))); - } + Authorization::skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([ + 'deploymentId' => $deployment->getId(), + 'deploymentInternalId' => $deployment->getSequence(), + ]))); }, $queries); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index f64a960507..76a5380f07 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform\Modules\Functions\Http\Deployments; use Appwrite\Event\Build; use Appwrite\Event\Event; use Appwrite\Extend\Exception; +use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -30,7 +31,7 @@ use Utopia\System\System; use Utopia\Validator\Boolean; use Utopia\Validator\Text; -class Create extends Action +class Create extends Base { use HTTP; @@ -81,6 +82,7 @@ class Create extends Action ->inject('request') ->inject('response') ->inject('dbForProject') + ->inject('dbForPlatform') ->inject('queueForEvents') ->inject('project') ->inject('deviceForFunctions') @@ -99,6 +101,7 @@ class Create extends Action Request $request, Response $response, Database $dbForProject, + Database $dbForPlatform, Event $queueForEvents, Document $project, Device $deviceForFunctions, @@ -300,6 +303,8 @@ class Create extends Action } } + $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); + $metadata = null; $queueForEvents diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index bbe84c56ee..e452ebf974 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -116,6 +116,7 @@ class Create extends Base project: $project, installation: $installation, dbForProject: $dbForProject, + dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, @@ -157,6 +158,9 @@ class Create extends Base ->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', '')); $dbForProject->updateDocument('functions', $function->getId(), $function); + + $this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform); + $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php index 0ad9852722..79e2e215ad 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php @@ -105,6 +105,7 @@ class Create extends Base project: $project, installation: $installation, dbForProject: $dbForProject, + dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index ec2a4baac5..b4300c4913 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -311,6 +311,7 @@ class Create extends Base project: $project, installation: $installation, dbForProject: $dbForProject, + dbForPlatform: $dbForPlatform, queueForBuilds: $queueForBuilds, template: $template, github: $github, diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index 318c2a2032..4bc3dca613 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -273,7 +273,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); + $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, new Document(), $github, true); } // Inform scheduler if function is still active diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 70546bf3f7..b2d48162e4 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -365,7 +365,7 @@ class Create extends Base } } - $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); $metadata = null; diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index 2407becd68..daf41e4c6d 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -196,7 +196,7 @@ class Create extends Base ])) ); - $this->updateManualRuleForNewSiteDeployment($project, $site, $deployment, $dbForPlatform); + $this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform); $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 72ec04a2a5..61d59d4da0 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -272,7 +272,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); + $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, new Document(), $github, true); } $queueForEvents->setParam('siteId', $site->getId()); diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 27b67d851d..1e817c49f9 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -409,4 +409,27 @@ trait FunctionsBase return $specifications; } + + protected function createFunctionRule(string $functionId, string $domain): mixed + { + $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/function', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'functionId' => $functionId, + 'domain' => $domain, + ]); + + return $rule; + } + + protected function getFunctionRule(string $ruleId): mixed + { + $rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + return $rule; + } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f5846af959..ee15a1d9e1 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -14,6 +14,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\System\System; class FunctionsCustomServerTest extends Scope { @@ -392,6 +393,10 @@ class FunctionsCustomServerTest extends Scope $functionId = $function['body']['$id'] ?? ''; + $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $rule = $this->createFunctionRule($functionId, $domain); + $this->assertEquals(201, $rule['headers']['status-code']); + $deployment = $this->createTemplateDeployment( $functionId, [ @@ -407,6 +412,17 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); + $rule = $this->getFunctionRule($rule['body']['$id']); + $this->assertEquals(200, $rule['headers']['status-code']); + $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString("Deployment is still building", $response['body']); + $deployment = $this->getDeployment($functionId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); @@ -524,6 +540,10 @@ class FunctionsCustomServerTest extends Scope */ $functionId = $data['functionId']; + $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + $rule = $this->createFunctionRule($functionId, $domain); + $this->assertEquals(201, $rule['headers']['status-code']); + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true @@ -535,6 +555,17 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); $this->assertEquals('index.js', $deployment['body']['entrypoint']); + $rule = $this->getFunctionRule($rule['body']['$id']); + $this->assertEquals(200, $rule['headers']['status-code']); + $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString("Deployment is still building", $response['body']); + $deploymentIdActive = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($functionId, $deploymentIdActive) { diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 3f82a42dca..f0a3ffb6dc 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -451,7 +451,7 @@ trait SitesBase return $specifications; } - protected function createRule(string $siteId, string $domain): mixed + protected function createSiteRule(string $siteId, string $domain): mixed { $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules/site', array_merge([ 'content-type' => 'application/json', @@ -464,7 +464,7 @@ trait SitesBase return $rule; } - protected function getRule(string $ruleId): mixed + protected function getSiteRule(string $ruleId): mixed { $rule = $this->client->call(Client::METHOD_GET, '/proxy/rules/' . $ruleId, array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index df29231562..3784747c73 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -861,7 +861,8 @@ class SitesCustomServerTest extends Scope $this->assertNotNull($siteId); - $rule = $this->createRule($siteId, \uniqid() . '.myapp.com'); + $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_SITES', ''); + $rule = $this->createSiteRule($siteId, $domain); $this->assertEquals(201, $rule['headers']['status-code']); $deployment = $this->createDeployment($siteId, [ @@ -875,10 +876,17 @@ class SitesCustomServerTest extends Scope $this->assertEquals('waiting', $deployment['body']['status']); $this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt'])); - $rule = $this->getRule($rule['body']['$id']); + $rule = $this->getSiteRule($rule['body']['$id']); $this->assertEquals(200, $rule['headers']['status-code']); $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString("Deployment is still building", $response['body']); + $deploymentIdActive = $deployment['body']['$id'] ?? ''; $this->assertEventually(function () use ($siteId, $deploymentIdActive) { @@ -1548,7 +1556,7 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } - #[Retry(count: 3)] + // #[Retry(count: 3)] public function testSiteTemplate(): void { $template = $this->getTemplate('playground-for-astro'); @@ -1570,7 +1578,8 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); - $rule = $this->createRule($siteId, \uniqid() . '.myapp.com'); + $domain = ID::unique() . '.' . System::getEnv('_APP_DOMAIN_SITES', ''); + $rule = $this->createSiteRule($siteId, $domain); $this->assertEquals(201, $rule['headers']['status-code']); $deployment = $this->createTemplateDeployment($siteId, [ @@ -1584,10 +1593,17 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $rule = $this->getRule($rule['body']['$id']); + $rule = $this->getSiteRule($rule['body']['$id']); $this->assertEquals(200, $rule['headers']['status-code']); $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString("Deployment is still building", $response['body']); + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); @@ -1599,10 +1615,6 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); - $domain = $this->setupSiteDomain($siteId); - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); From 73f5b4227d2c12da741ba0a9ff0f9e074aea3b1d Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 17 Nov 2025 14:58:04 +0530 Subject: [PATCH 4/8] nit --- src/Appwrite/Platform/Modules/Compute/Base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 02650b9b85..f88aec9101 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -346,7 +346,7 @@ class Base extends Action * In case of first deployment, deployment ID will be empty in the rules, so we need to update it here. * * @param \Utopia\Database\Document $project - * @param \Utopia\Database\Document $site + * @param \Utopia\Database\Document $resource * @param \Utopia\Database\Document $deployment * @param \Utopia\Database\Database $dbForPlatform * @return void From 0718f9040525912a97c4e8adb64610b9bd9d3378 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 17 Nov 2025 15:51:36 +0530 Subject: [PATCH 5/8] test failures due to merge --- .../Functions/FunctionsCustomServerTest.php | 14 +++--- .../Services/Sites/SitesCustomServerTest.php | 44 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 71c9826f3f..b49d9ba7e0 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -423,6 +423,7 @@ class FunctionsCustomServerTest extends Scope $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(400, $response['headers']['status-code']); $this->assertStringContainsString("Deployment is still building", $response['body']); + $this->assertStringContainsString("The page will update after the build completes.", $response['body']); $deployment = $this->getDeployment($functionId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); @@ -703,6 +704,7 @@ class FunctionsCustomServerTest extends Scope $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(400, $response['headers']['status-code']); $this->assertStringContainsString("Deployment is still building", $response['body']); + $this->assertStringContainsString('The page will update after the build completes.', $response['body']); $deploymentIdActive = $deployment['body']['$id'] ?? ''; @@ -2435,9 +2437,9 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ])); - $this->assertEquals(404, $response['headers']['status-code']); - $this->assertStringContainsString('No active deployments', $response['body']); - $this->assertStringContainsString('View deployments', $response['body']); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString('Deployment is still building', $response['body']); + $this->assertStringContainsString('The page will update after the build completes.', $response['body']); // canceled deployment $deployment = $this->createDeployment($functionId, [ @@ -2457,9 +2459,9 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'] ])); - $this->assertEquals(404, $response['headers']['status-code']); - $this->assertStringContainsString('No active deployments', $response['body']); - $this->assertStringContainsString('View deployments', $response['body']); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString('Deployment build canceled', $response['body']); + $this->assertStringContainsString('This build was canceled and won\'t be deployed.', $response['body']); $this->cleanupFunction($functionId); } diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index a48bdf7fc8..4b67169c03 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -886,6 +886,7 @@ class SitesCustomServerTest extends Scope $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(400, $response['headers']['status-code']); $this->assertStringContainsString("Deployment is still building", $response['body']); + $this->assertStringContainsString('The page will update after the build completes.', $response['body']); $deploymentIdActive = $deployment['body']['$id'] ?? ''; @@ -1556,7 +1557,7 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($siteId); } - // #[Retry(count: 3)] + #[Retry(count: 3)] public function testSiteTemplate(): void { $template = $this->getTemplate('playground-for-astro'); @@ -1594,6 +1595,18 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); + $rule = $this->getSiteRule($rule['body']['$id']); + $this->assertEquals(200, $rule['headers']['status-code']); + $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); + + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString("Deployment is still building", $response['body']); + $this->assertStringContainsString('The page will update after the build completes.', $response['body']); + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); @@ -1605,10 +1618,6 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); - $domain = $this->setupSiteDomain($siteId); - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); @@ -1743,17 +1752,6 @@ class SitesCustomServerTest extends Scope $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $rule = $this->getSiteRule($rule['body']['$id']); - $this->assertEquals(200, $rule['headers']['status-code']); - $this->assertEquals($deployment['body']['$id'], $rule['body']['deploymentId']); - - $proxyClient = new Client(); - $proxyClient->setEndpoint('http://' . $domain); - - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(400, $response['headers']['status-code']); - $this->assertStringContainsString("Deployment is still building", $response['body']); - $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); $this->assertEquals(200, $deployment['headers']['status-code']); $this->assertEquals(0, $deployment['body']['sourceSize']); @@ -1765,6 +1763,10 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); + $domain = $this->setupSiteDomain($siteId); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + $response = $proxyClient->call(Client::METHOD_GET, '/'); $this->assertEquals(200, $response['headers']['status-code']); @@ -2610,7 +2612,8 @@ class SitesCustomServerTest extends Scope }, 100000, 500); $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertStringContainsString('This page is empty, activate a deployment to make it live.', $response['body']); + $this->assertEquals(400, $response['headers']['status-code']); + $this->assertStringContainsString('Deployment build failed', $response['body']); $this->cleanupSite($siteId); } @@ -2740,13 +2743,6 @@ class SitesCustomServerTest extends Scope $this->assertStringContainsString("Deployment build canceled", $response['body']); $this->assertStringContainsString("View deployments", $response['body']); - // check site domain for no active deployments - $proxyClient->setEndpoint('http://' . $domain); - $response = $proxyClient->call(Client::METHOD_GET, '/'); - $this->assertEquals(404, $response['headers']['status-code']); - $this->assertStringContainsString('No active deployments', $response['body']); - $this->assertStringContainsString('View deployments', $response['body']); - $deployment = $this->createDeployment($siteId, [ 'code' => $this->packageSite('static-single-file'), 'activate' => 'true' From d2000ee9d8c394d84a1147ae06124db9b605f6a3 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 17 Nov 2025 17:58:15 +0530 Subject: [PATCH 6/8] test fix --- tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index b49d9ba7e0..cd4a130d30 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -435,6 +435,7 @@ class FunctionsCustomServerTest extends Scope }, 50000, 500); // Verify deployment sizes + $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertGreaterThan(0, $deployment['body']['sourceSize']); $this->assertGreaterThan(0, $deployment['body']['buildSize']); $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; From 3d69998f9caafbf86fceb8b1440d552ae98e45e3 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Mon, 17 Nov 2025 18:41:28 +0530 Subject: [PATCH 7/8] another test fix --- .../Services/Functions/FunctionsCustomServerTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index cd4a130d30..355fd01c8e 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -2443,6 +2443,17 @@ class FunctionsCustomServerTest extends Scope $this->assertStringContainsString('The page will update after the build completes.', $response['body']); // canceled deployment + $functionId = $this->setupFunction([ + 'functionId' => ID::unique(), + 'name' => 'Test Error Pages', + 'runtime' => 'node-22', + 'entrypoint' => 'index.js', + 'timeout' => 15, + 'commands' => 'cd non-existing-directory', + 'execute' => ['any'] + ]); + $domain = $this->setupFunctionDomain($functionId); + $proxyClient->setEndpoint('http://' . $domain); $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true From 9f7e3146a4dbcea54fe74a82938551a35f70bf71 Mon Sep 17 00:00:00 2001 From: Hemachandar Date: Tue, 18 Nov 2025 20:08:43 +0530 Subject: [PATCH 8/8] feedback --- app/controllers/api/vcs.php | 3 +++ src/Appwrite/Platform/Modules/Compute/Base.php | 5 ++++- .../Platform/Modules/Functions/Http/Functions/Create.php | 1 + tests/e2e/Services/Functions/FunctionsCustomServerTest.php | 6 ++++++ tests/e2e/Services/Sites/SitesCustomServerTest.php | 6 ++++++ 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 2ac6cd0b26..56bc72e708 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -5,6 +5,7 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Redirect; +use Appwrite\Platform\Modules\Compute\Base as ComputeBase; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Method; @@ -431,6 +432,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId } } + ComputeBase::updateEmptyManualRule($project, $resource, $deployment, $dbForPlatform); + if ($resource->getCollection() === 'sites' && !empty($latestCommentId) && !empty($previewRuleId)) { $retries = 0; $lockAcquired = false; diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index f88aec9101..cc9b403925 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -351,11 +351,14 @@ class Base extends Action * @param \Utopia\Database\Database $dbForPlatform * @return void */ - public function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform) + public static function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform) { + $resourceType = $resource->getCollection() === 'sites' ? 'site' : 'function'; + $queries = [ Query::equal('projectInternalId', [$project->getSequence()]), Query::equal('deploymentResourceInternalId', [$resource->getSequence()]), + Query::equal('deploymentResourceType', [$resourceType]), Query::equal('deploymentId', ['']), Query::equal('type', ['deployment']), Query::equal('trigger', ['manual']), diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index b4300c4913..b22710253e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -305,6 +305,7 @@ class Create extends Base $template = new Document(); $installation = $dbForPlatform->getDocument('installations', $function->getAttribute('installationId')); + // TODO: Is this still needed? Can this be removed? $deployment = $this->redeployVcsFunction( request: $request, function: $function, diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 355fd01c8e..74b6ed3e8d 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -2426,6 +2426,12 @@ class FunctionsCustomServerTest extends Scope $domain = $this->setupFunctionDomain($functionId); $proxyClient->setEndpoint('http://' . $domain); + $response = $proxyClient->call(Client::METHOD_GET, '/'); + + $this->assertEquals(404, $response['headers']['status-code']); + $this->assertStringContainsString('No active deployments', $response['body']); + $this->assertStringContainsString('View deployments', $response['body']); + $deployment = $this->createDeployment($functionId, [ 'code' => $this->packageFunction('basic'), 'activate' => true diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 4b67169c03..a3b3680b5c 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -2708,6 +2708,12 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); $domain = $this->setupSiteDomain($siteId); + $proxyClient->setEndpoint('http://' . $domain); + $response = $proxyClient->call(Client::METHOD_GET, '/'); + + $this->assertEquals(404, $response['headers']['status-code']); + $this->assertStringContainsString('No active deployments', $response['body']); + $this->assertStringContainsString('View deployments', $response['body']); // test canceled deployment error page $deployment = $this->createDeployment($siteId, [