Merge pull request #9364 from appwrite/feat-template-deployment

Feat: Template deployments
This commit is contained in:
Matej Bačo 2025-02-15 15:15:13 +01:00 committed by GitHub
commit 62b1c19357
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 383 additions and 145 deletions

2
composer.lock generated
View file

@ -8839,5 +8839,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}

View file

@ -19,7 +19,7 @@ use Utopia\VCS\Exception\RepositoryNotFound;
class Base extends Action
{
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github)
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github): Document
{
$deploymentId = ID::unique();
$entrypoint = $function->getAttribute('entrypoint', '');
@ -91,9 +91,11 @@ class Base extends Action
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
return $deployment;
}
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github)
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github): Document
{
$deploymentId = ID::unique();
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
@ -187,5 +189,7 @@ class Base extends Action
->setResource($site)
->setDeployment($deployment)
->setTemplate($template);
return $deployment;
}
}

View file

@ -0,0 +1,143 @@
<?php
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Template;
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\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Request;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
{
use HTTP;
public static function getName()
{
return 'createTemplateDeployment';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/functions/:functionId/deployments/template')
->desc('Create template deployment')
->groups(['api', 'functions'])
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
->label('audits.event', 'deployment.create')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk', new Method(
namespace: 'functions',
name: 'createTemplateDeployment',
description: <<<EOT
Create a deployment based on a template.
Use this endpoint with combination of [listTemplates](https://appwrite.io/docs/server/functions#listTemplates) to find the template details.
EOT,
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_DEPLOYMENT,
)
],
))
->param('functionId', '', new UID(), 'Function ID.')
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
->param('rootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.')
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('queueForEvents')
->inject('project')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
}
public function action(string $functionId, string $repository, string $owner, string $rootDirectory, string $version, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Event $queueForEvents, Document $project, Build $queueForBuilds, GitHub $github)
{
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$template = new Document([
'repositoryName' => $repository,
'ownerName' => $owner,
'rootDirectory' => $rootDirectory,
'version' => $version
]);
if (!empty($function->getAttribute('providerRepositoryId'))) {
$installation = $dbForPlatform->getDocument('installations', $function->getAttribute('installationId'));
$deployment = $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
return;
}
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
'resourceType' => 'functions',
'entrypoint' => $function->getAttribute('entrypoint', ''),
'commands' => $function->getAttribute('commands', ''),
'type' => 'manual',
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
'activate' => $activate,
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
}
}

View file

@ -2,7 +2,6 @@
namespace Appwrite\Platform\Modules\Functions\Http\Functions;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
@ -29,14 +28,12 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Roles;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Request;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
{
@ -90,30 +87,22 @@ class Create extends Base
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
$plan,
Config::getParam('runtime-specifications', []),
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
), 'Runtime specification for the function and builds.', true, ['plan'])
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('timelimit')
->inject('project')
->inject('user')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->callback([$this, 'action']);
}
public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github)
public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, callable $timelimit, Document $project, Event $queueForEvents, Database $dbForPlatform)
{
// Temporary abuse check
@ -153,20 +142,6 @@ class Create extends Base
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $runtime . '" is not supported');
}
// build from template
$template = new Document([]);
if (
!empty($templateRepository)
&& !empty($templateOwner)
&& !empty($templateRootDirectory)
&& !empty($templateVersion)
) {
$template->setAttribute('repositoryName', $templateRepository)
->setAttribute('ownerName', $templateOwner)
->setAttribute('rootDirectory', $templateRootDirectory)
->setAttribute('version', $templateVersion);
}
$installation = $dbForPlatform->getDocument('installations', $installationId);
if (!empty($installationId) && $installation->isEmpty()) {
@ -254,36 +229,6 @@ class Create extends Base
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
if (!empty($providerRepositoryId)) {
// Deploy VCS
$this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
} elseif (!$template->isEmpty()) {
// Deploy non-VCS from template
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
'resourceType' => 'functions',
'entrypoint' => $function->getAttribute('entrypoint', ''),
'commands' => $function->getAttribute('commands', ''),
'type' => 'manual',
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
'activate' => true,
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain)) {
$routeSubdomain = ID::unique();

View file

@ -3,6 +3,7 @@
namespace Appwrite\Platform\Modules\Functions\Services;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Create as CreateDeployment;
use Appwrite\Platform\Modules\Functions\Http\Deployments\Template\Create as CreateTemplateDeployment;
use Appwrite\Platform\Modules\Functions\Http\Functions\Create as CreateFunction;
use Appwrite\Platform\Modules\Functions\Http\Functions\Update as UpdateFunction;
use Appwrite\Platform\Modules\Functions\Http\Functions\XList as ListFunctions;
@ -19,5 +20,6 @@ class Http extends Service
$this->addAction(ListFunctions::getName(), new ListFunctions());
$this->addAction(ListRuntimes::getName(), new ListRuntimes());
$this->addAction(CreateDeployment::getName(), new CreateDeployment());
$this->addAction(CreateTemplateDeployment::getName(), new CreateTemplateDeployment());
}
}

View file

@ -0,0 +1,165 @@
<?php
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Template;
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\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Request;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
{
use HTTP;
public static function getName()
{
return 'createTemplateDeployment';
}
public function __construct()
{
$this
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/sites/:siteId/deployments/template')
->desc('Create deployment')
->groups(['api', 'sites'])
->label('scope', 'sites.write')
->label('event', 'sites.[siteId].deployments.[deploymentId].create')
->label('audits.event', 'deployment.create')
->label('audits.resource', 'site/{request.siteId}')
->label('sdk', new Method(
namespace: 'sites',
name: 'createTemplateDeployment',
description: <<<EOT
Create a deployment based on a template.
Use this endpoint with combination of [listTemplates](https://appwrite.io/docs/server/sites#listTemplates) to find the template details.
EOT,
auth: [AuthType::KEY],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_ACCEPTED,
model: Response::MODEL_DEPLOYMENT,
)
],
))
->param('siteId', '', new UID(), 'Site ID.')
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
->param('rootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.')
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('gitHub')
->callback([$this, 'action']);
}
public function action(string $siteId, string $repository, string $owner, string $rootDirectory, string $version, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github)
{
$site = $dbForProject->getDocument('sites', $siteId);
if ($site->isEmpty()) {
throw new Exception(Exception::SITE_NOT_FOUND);
}
$template = new Document([
'repositoryName' => $repository,
'ownerName' => $owner,
'rootDirectory' => $rootDirectory,
'version' => $version
]);
if (!empty($providerRepositoryId)) {
$installation = $dbForPlatform->getDocument('installations', $site->getAttribute('installationId'));
$deployment = $this->redeployVcsSite($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, $template, $github);
$queueForEvents
->setParam('siteId', $site->getId())
->setParam('deploymentId', $deployment->getId());
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
return;
}
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $site->getId(),
'resourceInternalId' => $site->getInternalId(),
'resourceType' => 'sites',
'installCommand' => $site->getAttribute('installCommand', ''),
'buildCommand' => $site->getAttribute('buildCommand', ''),
'outputDirectory' => $site->getAttribute('outputDirectory', ''),
'type' => 'manual',
'search' => implode(' ', [$deploymentId]),
'activate' => $activate,
]));
// Preview deployments url
$projectId = $project->getId();
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$previewDomain = "{$deploymentId}-{$projectId}.{$sitesDomain}";
$rule = Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => \md5($previewDomain),
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $previewDomain,
'resourceType' => 'deployment',
'resourceId' => $deploymentId,
'resourceInternalId' => $deployment->getInternalId(),
'status' => 'verified',
'certificateId' => '',
]))
);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment)
->setTemplate($template);
$queueForEvents
->setParam('siteId', $site->getId())
->setParam('deploymentId', $deployment->getId());
$response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
}
}

View file

@ -2,7 +2,6 @@
namespace Appwrite\Platform\Modules\Sites\Http\Sites;
use Appwrite\Event\Build;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Messaging\Adapter\Realtime;
@ -24,13 +23,11 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Request;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
{
@ -83,29 +80,21 @@ class Create extends Base
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true)
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true)
->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true)
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
->param('templateRootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.', true)
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.', true)
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new FrameworkSpecification(
$plan,
Config::getParam('framework-specifications', []),
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
), 'Framework specification for the site and builds.', true, ['plan'])
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->inject('queueForEvents')
->inject('queueForBuilds')
->inject('dbForPlatform')
->inject('gitHub')
->callback([$this, 'action']);
}
public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $subdomain, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github)
public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $subdomain, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForPlatform)
{
if (!empty($adapter)) {
$configFramework = Config::getParam('frameworks')[$framework] ?? [];
@ -139,20 +128,6 @@ class Create extends Base
throw new Exception(Exception::SITE_FRAMEWORK_UNSUPPORTED, 'Framework "' . $framework . '" is not supported');
}
// build from template
$template = new Document([]);
if (
!empty($templateRepository)
&& !empty($templateOwner)
&& !empty($templateRootDirectory)
&& !empty($templateVersion)
) {
$template->setAttribute('repositoryName', $templateRepository)
->setAttribute('ownerName', $templateOwner)
->setAttribute('rootDirectory', $templateRootDirectory)
->setAttribute('version', $templateVersion);
}
$installation = $dbForPlatform->getDocument('installations', $installationId);
if (!empty($installationId) && $installation->isEmpty()) {
@ -220,57 +195,6 @@ class Create extends Base
$site = $dbForProject->updateDocument('sites', $site->getId(), $site);
if (!empty($providerRepositoryId)) {
// Deploy VCS
$this->redeployVcsSite($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, $template, $github);
} elseif (!$template->isEmpty()) {
// Deploy non-VCS from template
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $site->getId(),
'resourceInternalId' => $site->getInternalId(),
'resourceType' => 'sites',
'installCommand' => $site->getAttribute('installCommand', ''),
'buildCommand' => $site->getAttribute('buildCommand', ''),
'outputDirectory' => $site->getAttribute('outputDirectory', ''),
'type' => 'manual',
'search' => implode(' ', [$deploymentId]),
'activate' => true,
]));
// Preview deployments url
$projectId = $project->getId();
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
$previewDomain = "{$deploymentId}-{$projectId}.{$sitesDomain}";
$rule = Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => \md5($previewDomain),
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'domain' => $previewDomain,
'resourceType' => 'deployment',
'resourceId' => $deploymentId,
'resourceInternalId' => $deployment->getInternalId(),
'status' => 'verified',
'certificateId' => '',
]))
);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
->setDeployment($deployment)
->setTemplate($template);
}
if (!empty($sitesDomain)) {
$rule = Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([

View file

@ -9,6 +9,7 @@ use Appwrite\Platform\Modules\Sites\Http\Deployments\Create as CreateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Delete as DeleteDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Download\Get as DownloadDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Get as GetDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Template\Create as CreateTemplateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\Update as UpdateDeployment;
use Appwrite\Platform\Modules\Sites\Http\Deployments\XList as ListDeployments;
use Appwrite\Platform\Modules\Sites\Http\Frameworks\XList as ListFrameworks;
@ -49,6 +50,7 @@ class Http extends Service
// Deployments
$this->addAction(CreateDeployment::getName(), new CreateDeployment());
$this->addAction(CreateTemplateDeployment::getName(), new CreateTemplateDeployment());
$this->addAction(GetDeployment::getName(), new GetDeployment());
$this->addAction(ListDeployments::getName(), new ListDeployments());
$this->addAction(UpdateDeployment::getName(), new UpdateDeployment());

View file

@ -206,6 +206,16 @@ trait FunctionsBase
return $deployment;
}
protected function createTemplateDeployment(string $functionId, mixed $params = []): mixed
{
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments/template', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
return $deployment;
}
protected function getFunctionUsage(string $functionId, mixed $params): mixed
{
$usage = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([

View file

@ -358,6 +358,21 @@ class FunctionsCustomServerTest extends Scope
$functionId = $function['body']['$id'] ?? '';
$deployment = $this->createTemplateDeployment(
$functionId,
[
'functionId' => ID::unique(),
'activate' => true,
'repository' => $starterTemplate['body']['providerRepositoryId'],
'owner' => $starterTemplate['body']['providerOwner'],
'rootDirectory' => $phpRuntime['providerRootDirectory'],
'version' => $starterTemplate['body']['providerVersion'],
]
);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deployments = $this->listDeployments($functionId);
$this->assertEquals(200, $deployments['headers']['status-code']);

View file

@ -228,6 +228,16 @@ trait SitesBase
return $deployment;
}
protected function createTemplateDeployment(string $siteId, mixed $params = []): mixed
{
$deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments/template', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), $params);
return $deployment;
}
protected function getSiteUsage(string $siteId, mixed $params): mixed
{
$usage = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/usage', array_merge([

View file

@ -566,11 +566,6 @@ class SitesCustomServerTest extends Scope
'installCommand' => $nextjsFramework['installCommand'],
'outputDirectory' => $nextjsFramework['outputDirectory'],
'providerRootDirectory' => $nextjsFramework['providerRootDirectory'],
'templateOwner' => $starterTemplate['body']['providerOwner'],
'templateRepository' => $starterTemplate['body']['providerRepositoryId'],
'templateRootDirectory' => $nextjsFramework['providerRootDirectory'],
'templateVersion' => $starterTemplate['body']['providerVersion'],
'providerBranch' => 'main',
]
);
@ -579,6 +574,20 @@ class SitesCustomServerTest extends Scope
$siteId = $site['body']['$id'] ?? '';
$deployment = $this->createTemplateDeployment(
$siteId,
[
'owner' => $starterTemplate['body']['providerOwner'],
'repository' => $starterTemplate['body']['providerRepositoryId'],
'rootDirectory' => $nextjsFramework['providerRootDirectory'],
'version' => $starterTemplate['body']['providerVersion'],
'activate' => true,
]
);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deployments = $this->listDeployments($siteId);
$this->assertEquals(200, $deployments['headers']['status-code']);
@ -1214,12 +1223,21 @@ class SitesCustomServerTest extends Scope
'buildCommand' => $template['frameworks'][0]['buildCommand'],
'installCommand' => $template['frameworks'][0]['installCommand'],
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
'templateRepository' => $template['providerRepositoryId'],
'templateOwner' => $template['providerOwner'],
'templateRootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
'templateVersion' => $template['providerVersion'],
]);
$this->assertNotEmpty($siteId);
$deployment = $this->createTemplateDeployment($siteId, [
'repository' => $template['providerRepositoryId'],
'owner' => $template['providerOwner'],
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
'version' => $template['providerVersion'],
'activate' => true
]);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$this->assertEventually(function () use ($siteId) {
$site = $this->getSite($siteId);
$this->assertNotEmpty($site['body']['deploymentId']);