Merge pull request #10486 from appwrite/feat-add-branch-deployments

Add branch-only deployments to Sites without ref tags
This commit is contained in:
Matej Bačo
2025-11-13 10:33:46 +01:00
committed by GitHub
14 changed files with 575 additions and 65 deletions
+34 -8
View File
@@ -14363,10 +14363,22 @@
"description": "Path to function code in the template repo.",
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the function template.",
"x-example": "<VERSION>"
"description": "Type for the reference provided. Can be commit, branch, or tag",
"x-example": "commit",
"enum": [
"commit",
"branch",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -14378,7 +14390,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
@@ -32184,10 +32197,22 @@
"description": "Path to site code in the template repo.",
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the site template.",
"x-example": "<VERSION>"
"description": "Type for the reference provided. Can be commit, branch, or tag",
"x-example": "branch",
"enum": [
"branch",
"commit",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -32199,7 +32224,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
+34 -8
View File
@@ -13152,10 +13152,22 @@
"description": "Path to function code in the template repo.",
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the function template.",
"x-example": "<VERSION>"
"description": "Type for the reference provided. Can be commit, branch, or tag",
"x-example": "commit",
"enum": [
"commit",
"branch",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -13167,7 +13179,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
@@ -22711,10 +22724,22 @@
"description": "Path to site code in the template repo.",
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the site template.",
"x-example": "<VERSION>"
"description": "Type for the reference provided. Can be commit, branch, or tag",
"x-example": "branch",
"enum": [
"branch",
"commit",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -22726,7 +22751,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
+36 -8
View File
@@ -14306,11 +14306,24 @@
"default": null,
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the function template.",
"description": "Type for the reference provided. Can be commit, branch, or tag",
"default": null,
"x-example": "<VERSION>"
"x-example": "commit",
"enum": [
"commit",
"branch",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"default": null,
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -14323,7 +14336,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
@@ -32292,11 +32306,24 @@
"default": null,
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the site template.",
"description": "Type for the reference provided. Can be commit, branch, or tag",
"default": null,
"x-example": "<VERSION>"
"x-example": "branch",
"enum": [
"branch",
"commit",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"default": null,
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -32309,7 +32336,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
+36 -8
View File
@@ -13122,11 +13122,24 @@
"default": null,
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the function template.",
"description": "Type for the reference provided. Can be commit, branch, or tag",
"default": null,
"x-example": "<VERSION>"
"x-example": "commit",
"enum": [
"commit",
"branch",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"default": null,
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -13139,7 +13152,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
@@ -22867,11 +22881,24 @@
"default": null,
"x-example": "<ROOT_DIRECTORY>"
},
"version": {
"type": {
"type": "string",
"description": "Version (tag) for the repo linked to the site template.",
"description": "Type for the reference provided. Can be commit, branch, or tag",
"default": null,
"x-example": "<VERSION>"
"x-example": "branch",
"enum": [
"branch",
"commit",
"tag"
],
"x-enum-name": null,
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "Reference value, can be a commit hash, branch name, or release tag",
"default": null,
"x-example": "<REFERENCE>"
},
"activate": {
"type": "boolean",
@@ -22884,7 +22911,8 @@
"repository",
"owner",
"rootDirectory",
"version"
"type",
"reference"
]
}
}
+4
View File
@@ -23,6 +23,7 @@ use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
@@ -906,6 +907,9 @@ App::init()
$dbForProject = $getProjectDB($project);
$request->addFilter(new RequestV20($dbForProject, $route->getPathValues($request)));
}
if (version_compare($requestFormat, '1.9.0', '<')) {
$request->addFilter(new RequestV21());
}
}
$domain = $request->getHostname();
@@ -21,6 +21,7 @@ use Utopia\Platform\Scope\HTTP;
use Utopia\Swoole\Request;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
@@ -65,7 +66,8 @@ class Create extends Base
->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('type', '', new WhiteList(['commit', 'branch', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag')
->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
@@ -83,7 +85,8 @@ class Create extends Base
string $repository,
string $owner,
string $rootDirectory,
string $version,
string $type,
string $reference,
bool $activate,
Request $request,
Response $response,
@@ -100,11 +103,16 @@ class Create extends Base
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$branchUrl = "https://github.com/$owner/$repository/blob/$reference";
$repositoryUrl = "https://github.com/$owner/$repository";
$template = new Document([
'repositoryName' => $repository,
'ownerName' => $owner,
'rootDirectory' => $rootDirectory,
'version' => $version
'referenceType' => $type,
'referenceValue' => $reference,
]);
if (!empty($function->getAttribute('providerRepositoryId'))) {
@@ -146,7 +154,12 @@ class Create extends Base
'resourceType' => 'functions',
'entrypoint' => $function->getAttribute('entrypoint', ''),
'buildCommands' => $function->getAttribute('commands', ''),
'type' => 'manual',
'providerRepositoryName' => $repository,
'providerRepositoryOwner' => $owner,
'providerRepositoryUrl' => $repositoryUrl,
'providerBranchUrl' => $branchUrl,
'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '',
'type' => 'vcs',
'activate' => $activate,
]));
@@ -310,20 +310,23 @@ class Builds extends Action
// Non-VCS + Template
$templateRepositoryName = $template->getAttribute('repositoryName', '');
$templateOwnerName = $template->getAttribute('ownerName', '');
$templateVersion = $template->getAttribute('version', '');
$templateReferenceType = $template->getAttribute('referenceType', '');
$templateReferenceValue = $template->getAttribute('referenceValue', '');
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateReferenceType) && !empty($templateReferenceValue)) {
$stdout = '';
$stderr = '';
// Clone template repo
$tmpTemplateDirectory = '/tmp/builds/' . $deploymentId . '-template';
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateReferenceValue, $templateReferenceType, $tmpTemplateDirectory, $templateRootDirectory);
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
if ($exit !== 0) {
@@ -23,6 +23,7 @@ use Utopia\Swoole\Request;
use Utopia\System\System;
use Utopia\Validator\Boolean;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
use Utopia\VCS\Adapter\Git\GitHub;
class Create extends Base
@@ -67,7 +68,8 @@ class Create extends Base
->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('type', '', new WhiteList(['branch', 'commit', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag')
->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
@@ -85,7 +87,8 @@ class Create extends Base
string $repository,
string $owner,
string $rootDirectory,
string $version,
string $type,
string $reference,
bool $activate,
Request $request,
Response $response,
@@ -102,11 +105,15 @@ class Create extends Base
throw new Exception(Exception::SITE_NOT_FOUND);
}
$branchUrl = "https://github.com/$owner/$repository/blob/$reference";
$repositoryUrl = "https://github.com/$owner/$repository";
$template = new Document([
'repositoryName' => $repository,
'ownerName' => $owner,
'rootDirectory' => $rootDirectory,
'version' => $version
'referenceType' => $type,
'referenceValue' => $reference
]);
if (!empty($site->getAttribute('providerRepositoryId'))) {
@@ -157,9 +164,14 @@ class Create extends Base
'resourceType' => 'sites',
'buildCommands' => \implode(' && ', $commands),
'buildOutput' => $site->getAttribute('outputDirectory', ''),
'providerRepositoryName' => $repository,
'providerRepositoryOwner' => $owner,
'providerRepositoryUrl' => $repositoryUrl,
'providerBranchUrl' => $branchUrl,
'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '',
'adapter' => $site->getAttribute('adapter', ''),
'fallbackFile' => $site->getAttribute('fallbackFile', ''),
'type' => 'manual',
'type' => 'vcs',
'activate' => $activate,
]));
@@ -0,0 +1,34 @@
<?php
namespace Appwrite\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
class V21 extends Filter
{
// Convert 1.8.0 params to 1.8.1
public function parse(array $content, string $model): array
{
switch ($model) {
case 'functions.createTemplateDeployment':
case 'sites.createTemplateDeployment':
$content = $this->convertVersionToTypeAndReference($content);
break;
}
return $content;
}
/**
* Convert version parameter to type and reference for backwards compatibility
* with 1.8.0 template deployment endpoints
*/
protected function convertVersionToTypeAndReference(array $content): array
{
if (!empty($content['version'])) {
$content['type'] = 'tag';
$content['reference'] = $content['version'];
unset($content['version']);
}
return $content;
}
}
+1
View File
@@ -28,6 +28,7 @@ class UsageTest extends Scope
FunctionsBase::createVariable insteadof SitesBase;
FunctionsBase::getVariable insteadof SitesBase;
FunctionsBase::listVariables insteadof SitesBase;
FunctionsBase::helperGetLatestCommit insteadof SitesBase;
FunctionsBase::updateVariable insteadof SitesBase;
FunctionsBase::deleteVariable insteadof SitesBase;
FunctionsBase::getDeployment insteadof SitesBase;
@@ -271,6 +271,29 @@ trait FunctionsBase
return $template;
}
protected function helperGetLatestCommit(string $owner, string $repository): ?string
{
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'User-Agent: Appwrite',
'Accept: application/vnd.github.v3+json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$commitData = json_decode($response, true);
if (isset($commitData['sha'])) {
return $commitData['sha'];
}
}
return null;
}
protected function createExecution(string $functionId, mixed $params = []): mixed
{
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
@@ -361,7 +361,7 @@ class FunctionsCustomServerTest extends Scope
$starterTemplate = $this->getTemplate('starter');
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
$phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
return $runtime['name'] === 'node-22';
}))[0];
@@ -374,15 +374,15 @@ class FunctionsCustomServerTest extends Scope
'name' => $starterTemplate['body']['name'],
'runtime' => 'node-22',
'execute' => $starterTemplate['body']['permissions'],
'entrypoint' => $phpRuntime['entrypoint'],
'entrypoint' => $runtime['entrypoint'],
'events' => $starterTemplate['body']['events'],
'schedule' => $starterTemplate['body']['cron'],
'timeout' => $starterTemplate['body']['timeout'],
'commands' => $phpRuntime['commands'],
'commands' => $runtime['commands'],
'scopes' => $starterTemplate['body']['scopes'],
'templateRepository' => $starterTemplate['body']['providerRepositoryId'],
'templateOwner' => $starterTemplate['body']['providerOwner'],
'templateRootDirectory' => $phpRuntime['providerRootDirectory'],
'templateRootDirectory' => $runtime['providerRootDirectory'],
'templateVersion' => $starterTemplate['body']['providerVersion'],
]
);
@@ -399,19 +399,29 @@ class FunctionsCustomServerTest extends Scope
'activate' => true,
'repository' => $starterTemplate['body']['providerRepositoryId'],
'owner' => $starterTemplate['body']['providerOwner'],
'rootDirectory' => $phpRuntime['providerRootDirectory'],
'version' => $starterTemplate['body']['providerVersion'],
'rootDirectory' => $runtime['providerRootDirectory'],
'type' => 'tag',
'reference' => $starterTemplate['body']['providerVersion'],
]
);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deployment = $this->getDeployment($functionId, $deployment['body']['$id']);
// Wait for deployment to be ready
$deploymentId = $deployment['body']['$id'];
$this->assertEventually(function () use ($functionId, $deploymentId) {
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals('ready', $deployment['body']['status']);
}, 50000, 500);
// Verify deployment sizes
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals(0, $deployment['body']['sourceSize']);
$this->assertEquals(0, $deployment['body']['buildSize']);
$this->assertEquals(0, $deployment['body']['totalSize']);
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
$deployments = $this->listDeployments($functionId);
@@ -433,16 +443,7 @@ class FunctionsCustomServerTest extends Scope
$lastDeployment = $deployments['body']['deployments'][0];
$this->assertNotEmpty($lastDeployment['$id']);
$this->assertEquals(0, $lastDeployment['sourceSize']);
$deploymentId = $lastDeployment['$id'];
$this->assertEventually(function () use ($functionId, $deploymentId) {
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals('ready', $deployment['body']['status']);
}, 50000, 1000);
$this->assertGreaterThan(0, $lastDeployment['sourceSize']);
$function = $this->getFunction($functionId);
@@ -511,7 +512,144 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals($deployment['body']['$id'], $function['body']['deploymentId']);
$this->assertEquals($deployment['body']['$createdAt'], $function['body']['deploymentCreatedAt']);
$function = $this->cleanupFunction($functionId);
$this->cleanupFunction($functionId);
}
public function testCreateFunctionAndDeploymentFromTemplateBranch()
{
$starterTemplate = $this->getTemplate('starter');
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
return $runtime['name'] === 'node-22';
}))[0];
// If this fails, the template has variables, and this test needs to be updated
$this->assertEmpty($starterTemplate['body']['variables']);
$function = $this->createFunction(
[
'functionId' => ID::unique(),
'name' => $starterTemplate['body']['name'] . ' - Branch Test',
'runtime' => 'node-22',
'execute' => $starterTemplate['body']['permissions'],
'entrypoint' => $runtime['entrypoint'],
'events' => $starterTemplate['body']['events'],
'schedule' => $starterTemplate['body']['cron'],
'timeout' => $starterTemplate['body']['timeout'],
'commands' => $runtime['commands'],
'scopes' => $starterTemplate['body']['scopes'],
]
);
$this->assertEquals(201, $function['headers']['status-code']);
$this->assertNotEmpty($function['body']['$id']);
$functionId = $function['body']['$id'] ?? '';
// Deploy using branch
$deployment = $this->createTemplateDeployment(
$functionId,
[
'resourceId' => ID::unique(),
'activate' => true,
'repository' => $starterTemplate['body']['providerRepositoryId'],
'owner' => $starterTemplate['body']['providerOwner'],
'rootDirectory' => $runtime['providerRootDirectory'],
'type' => 'branch',
'reference' => 'main',
]
);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deploymentId = $deployment['body']['$id'];
$this->assertEventually(function () use ($functionId, $deploymentId) {
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals('ready', $deployment['body']['status']);
}, 50000, 500);
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
$this->cleanupFunction($functionId);
}
public function testCreateFunctionAndDeploymentFromTemplateCommit()
{
$starterTemplate = $this->getTemplate('starter');
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
// Get latest commit using helper function
$latestCommit = $this->helperGetLatestCommit(
$starterTemplate['body']['providerOwner'],
$starterTemplate['body']['providerRepositoryId']
);
$this->assertNotNull($latestCommit);
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
return $runtime['name'] === 'node-22';
}))[0];
// If this fails, the template has variables, and this test needs to be updated
$this->assertEmpty($starterTemplate['body']['variables']);
$function = $this->createFunction(
[
'functionId' => ID::unique(),
'name' => $starterTemplate['body']['name'] . ' - Commit Test',
'runtime' => 'node-22',
'execute' => $starterTemplate['body']['permissions'],
'entrypoint' => $runtime['entrypoint'],
'events' => $starterTemplate['body']['events'],
'schedule' => $starterTemplate['body']['cron'],
'timeout' => $starterTemplate['body']['timeout'],
'commands' => $runtime['commands'],
'scopes' => $starterTemplate['body']['scopes'],
]
);
$this->assertEquals(201, $function['headers']['status-code']);
$this->assertNotEmpty($function['body']['$id']);
$functionId = $function['body']['$id'] ?? '';
// Deploy using commit
$deployment = $this->createTemplateDeployment(
$functionId,
[
'resourceId' => ID::unique(),
'activate' => true,
'repository' => $starterTemplate['body']['providerRepositoryId'],
'owner' => $starterTemplate['body']['providerOwner'],
'rootDirectory' => $runtime['providerRootDirectory'],
'type' => 'commit',
'reference' => $latestCommit,
]
);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deploymentId = $deployment['body']['$id'];
$this->assertEventually(function () use ($functionId, $deploymentId) {
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals('ready', $deployment['body']['status']);
}, 50000, 500);
$deployment = $this->getDeployment($functionId, $deploymentId);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
$this->cleanupFunction($functionId);
}
/**
+24
View File
@@ -329,9 +329,33 @@ trait SitesBase
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]);
return $template;
}
protected function helperGetLatestCommit(string $owner, string $repository): ?string
{
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'User-Agent: Appwrite',
'Accept: application/vnd.github.v3+json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$commitData = json_decode($response, true);
if (isset($commitData['sha'])) {
return $commitData['sha'];
}
}
return null;
}
protected function deleteSite(string $siteId): mixed
{
$site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([
@@ -1567,7 +1567,157 @@ class SitesCustomServerTest extends Scope
'repository' => $template['providerRepositoryId'],
'owner' => $template['providerOwner'],
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
'version' => $template['providerVersion'],
'type' => 'tag',
'reference' => $template['providerVersion'],
'activate' => true
]);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals(0, $deployment['body']['sourceSize']);
$this->assertEquals(0, $deployment['body']['buildSize']);
$this->assertEquals(0, $deployment['body']['totalSize']);
$this->assertEventually(function () use ($siteId) {
$site = $this->getSite($siteId);
$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']);
$this->assertStringContainsString("Astro Blog", $response['body']);
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
$response = $proxyClient->call(Client::METHOD_GET, '/about');
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertStringContainsString("Astro Blog", $response['body']);
$this->assertStringContainsString("About Me", $response['body']);
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
$this->cleanupSite($siteId);
}
public function testCreateSiteFromTemplateBranch()
{
$template = $this->getTemplate('playground-for-astro');
$this->assertEquals(200, $template['headers']['status-code']);
$template = $template['body'];
$siteId = $this->setupSite([
'siteId' => ID::unique(),
'name' => 'Astro Blog - Branch Test',
'framework' => $template['frameworks'][0]['key'],
'adapter' => $template['frameworks'][0]['adapter'],
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
'buildCommand' => $template['frameworks'][0]['buildCommand'],
'installCommand' => $template['frameworks'][0]['installCommand'],
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
]);
$this->assertNotEmpty($siteId);
// Deploy using branch
$deployment = $this->createTemplateDeployment($siteId, [
'repository' => $template['providerRepositoryId'],
'owner' => $template['providerOwner'],
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
'type' => 'branch',
'reference' => 'main',
'activate' => true
]);
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertEquals(0, $deployment['body']['sourceSize']);
$this->assertEquals(0, $deployment['body']['buildSize']);
$this->assertEquals(0, $deployment['body']['totalSize']);
$this->assertEventually(function () use ($siteId) {
$site = $this->getSite($siteId);
$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']);
$this->assertStringContainsString("Astro Blog", $response['body']);
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
$response = $proxyClient->call(Client::METHOD_GET, '/about');
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertStringContainsString("Astro Blog", $response['body']);
$this->assertStringContainsString("About Me", $response['body']);
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
$this->assertEquals(200, $deployment['headers']['status-code']);
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
$this->cleanupSite($siteId);
}
public function testCreateSiteFromTemplateCommit()
{
$template = $this->getTemplate('playground-for-astro');
$this->assertEquals(200, $template['headers']['status-code']);
// Get latest commit using helper function
$latestCommit = $this->helperGetLatestCommit(
$template['body']['providerOwner'],
$template['body']['providerRepositoryId']
);
$this->assertNotNull($latestCommit);
$template = $template['body'];
$siteId = $this->setupSite([
'siteId' => ID::unique(),
'name' => 'Astro Blog - Commit Test',
'framework' => $template['frameworks'][0]['key'],
'adapter' => $template['frameworks'][0]['adapter'],
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
'buildCommand' => $template['frameworks'][0]['buildCommand'],
'installCommand' => $template['frameworks'][0]['installCommand'],
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
]);
$this->assertNotEmpty($siteId);
// Deploy using commit
$deployment = $this->createTemplateDeployment($siteId, [
'repository' => $template['providerRepositoryId'],
'owner' => $template['providerOwner'],
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
'type' => 'commit',
'reference' => $latestCommit,
'activate' => true
]);