From 622ae009bd0769117efafd5cfe1893160820ae67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sat, 8 Feb 2025 21:22:36 +0100 Subject: [PATCH] Implement ZIP site deployment --- Dockerfile | 3 ++ .../Modules/Functions/Workers/Builds.php | 41 +++++++++++++++--- .../Modules/Sites/Http/Deployments/Create.php | 7 ++- tests/e2e/Services/Sites/SitesBase.php | 37 ++++++++++++---- .../Services/Sites/SitesCustomServerTest.php | 41 ++++++++++++++++++ tests/resources/sites/static/code.zip | Bin 0 -> 793 bytes 6 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 tests/resources/sites/static/code.zip diff --git a/Dockerfile b/Dockerfile index 41810f5dc4..9983160a2c 100755 --- a/Dockerfile +++ b/Dockerfile @@ -30,6 +30,9 @@ RUN \ RUN apk add libwebp +# TODO: Move to appwrite/docker-base +RUN apk add zip + WORKDIR /usr/src/code COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 41ab2fe02a..43e211e4ca 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -261,8 +261,16 @@ class Builds extends Action Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr); $directorySize = $deviceForFunctions->getFileSize($source); - $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source)->setAttribute('size', $directorySize)); + + $build = $build->setAttribute('source', $source); + + $deployment = $deployment + ->setAttribute('path', $source) + ->setAttribute('size', $directorySize) + ->setAttribute('metadata', ['content_type' => 'application/gzip']); + + $build = $dbForProject->updateDocument('builds', $build->getId(), $build); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); } } elseif ($isNewBuild && $isVcsEnabled) { // VCS and VCS+Temaplte @@ -417,10 +425,16 @@ class Builds extends Action Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $stdout, $stderr); - $build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source)); - $directorySize = $deviceForFunctions->getFileSize($source); - $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source)->setAttribute('size', $directorySize)); + + $build = $build->setAttribute('source', $source); + $deployment = $build + ->setAttribute('path', $source) + ->setAttribute('size', $directorySize) + ->setAttribute('metadata', ['content_type' => 'application/gzip']); + + $build = $dbForProject->updateDocument('builds', $build->getId(), $build); + $deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment); $this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform); } @@ -565,7 +579,22 @@ class Builds extends Action Co::join([ Co\go(function () use ($executor, &$response, $project, $deployment, $source, $resource, $runtime, $vars, $command, $cpus, $memory, $timeout, &$err, $version) { try { - $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"'; + $contentType = $deployment->getAttribute('metadata', [])['content_type'] ?? ''; + + $extractCommand = ''; + switch ($contentType) { + case 'application/zip': + $extractCommand = 'unzip /tmp/code.tar.gz -d /mnt/code'; + break; + case 'application/gzip': + $extractCommand = 'tar -zxf /tmp/code.tar.gz -C /mnt/code'; + break; + default: + throw new \Exception('Unsupported deployment content type: ' . $contentType); + break; + } + + $command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : ($extractCommand . ' && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"'); // TODO: Detect adapter if adapter is empty $response = $executor->createRuntime( diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 38b9e2aeba..9b388b8544 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -119,7 +119,7 @@ class Create extends Action throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } - $fileExt = new FileExt([FileExt::TYPE_GZIP]); + $fileExt = new FileExt([FileExt::TYPE_GZIP, 'zip']); // TODO: Move 'zip' to Storage library $fileSizeValidator = new FileSize(System::getEnv('_APP_COMPUTE_SIZE_LIMIT', '30000000')); $upload = new Upload(); @@ -172,7 +172,9 @@ class Create extends Action $deployment = $dbForProject->getDocument('deployments', $deploymentId); $metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)]; + \var_dump($metadata); if (!$deployment->isEmpty()) { + \var_dump("Setting"); $chunks = $deployment->getAttribute('chunksTotal', 1); $metadata = $deployment->getAttribute('metadata', []); if ($chunk === -1) { @@ -206,6 +208,7 @@ class Create extends Action $fileSize = $deviceForFunctions->getFileSize($path); if ($deployment->isEmpty()) { + \var_dump("Creating"); $deployment = $dbForProject->createDocument('deployments', new Document([ '$id' => $deploymentId, '$permissions' => [ @@ -249,6 +252,7 @@ class Create extends Action ])) ); } else { + \var_dump("updating"); $deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize)->setAttribute('metadata', $metadata)); } @@ -259,6 +263,7 @@ class Create extends Action ->setDeployment($deployment); } else { if ($deployment->isEmpty()) { + \var_dump("Creating 2"); $deployment = $dbForProject->createDocument('deployments', new Document([ '$id' => $deploymentId, '$permissions' => [ diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 277064ead5..0c1036dea1 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -12,9 +12,6 @@ trait SitesBase { use Async; - protected string $stdout = ''; - protected string $stderr = ''; - protected function setupSite(mixed $params): string { $site = $this->client->call(Client::METHOD_POST, '/sites', array_merge([ @@ -164,18 +161,42 @@ trait SitesBase return $logs; } - protected function packageSite(string $site): CURLFile + protected function packageSite(string $site, string $format = 'gzip'): CURLFile { + $extension = ''; + $command = ''; + $header = ''; + + switch ($format) { + case 'gzip': + $extension = 'tar.gz'; + $command = 'tar --exclude code.tar.gz -czf code.tar.gz .'; + $header = 'application/x-gzip'; + break; + case 'zip': + $extension = 'zip'; + $command = 'zip -x code.zip -r code.zip .'; + $header = 'application/zip'; + break; + default: + throw new \Exception('Invalid package format'); + } + $folderPath = realpath(__DIR__ . '/../../../resources/sites') . "/$site"; - $tarPath = "$folderPath/code.tar.gz"; + $filePath = "$folderPath/code." . $extension; - Console::execute("cd $folderPath && tar --exclude code.tar.gz -czf code.tar.gz .", '', $this->stdout, $this->stderr); + $stdout = ''; + $stderr = ''; + $exitCode = Console::execute("cd $folderPath && " . $command, '', $stdout, $stderr); - if (filesize($tarPath) > 1024 * 1024 * 5) { + $this->assertEquals(0, $exitCode); + $this->assertEmpty($stderr); + + if (filesize($filePath) > 1024 * 1024 * 5) { throw new \Exception('Code package is too large. Use the chunked upload method instead.'); } - return new CURLFile($tarPath, 'application/x-gzip', \basename($tarPath)); + return new CURLFile($filePath, $header, \basename($filePath)); } protected function createDeployment(string $siteId, mixed $params = []): mixed diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index ff3aa7b348..1d0d8655cb 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1088,5 +1088,46 @@ class SitesCustomServerTest extends Scope $this->cleanupSite($site['body']['$id']); } + public function testSiteZipDeployment(): void + { + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'A site', + 'framework' => 'other', + 'adapter' => 'static', + 'buildRuntime' => 'static-1', + 'outputDirectory' => './', + 'buildCommand' => '', + 'installCommand' => '', + 'fallbackFile' => '', + ]); + + $this->assertNotEmpty($siteId); + + $deploymentId = $this->setupDeployment($siteId, [ + 'code' => $this->packageSite('static', 'zip'), + 'activate' => 'true' + ]); + + $this->assertNotEmpty($deploymentId); + + $domain = $this->getSiteDomain($siteId); + + $this->assertNotEmpty($domain); + + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ])); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Hello Appwrite", $response['body']); + + $this->cleanupSite($siteId); + } + // TODO: Add tests for deletion of resources when site is deleted } diff --git a/tests/resources/sites/static/code.zip b/tests/resources/sites/static/code.zip new file mode 100644 index 0000000000000000000000000000000000000000..01b56d801b4eafed2f13d101533b4871036a0daa GIT binary patch literal 793 zcmWIWW@Zs#U|`^2c#`ZMwQAGsyv0D?DHXpx+uq&~ zT&$I59(=T>f0z63KRP=;_4|c6rK_!cxm7#>=uIw=gBfO*c|<)G^w?m=$iM)?+(2(8 z=clCVl_VDFrB{Ldc{G1{x~ti8bbrcsbAXLtV19e)v|o#X2wTE68`o6b>3rtm2Av;; z%6CbK>uJyNm>i@QkpAVZ?t+spi}O^D{ivVqe0zU@1!kPD*lyX z$8DsFE6*9XFFh@Pwor~;x$4-QeXq}i?|gsl{|bdEyOu3qV7NBwm1J`BW|f~?{ul0= z^;`aV*=$1&*nD!$C$lcf;-sBeu`XX{v3tC^|5K9 zx{?Q1xqV@td1?3Em|yDm9!?U|oqEsjQr+#Z$C5*r^JYrlJ$=h0)z><5qCIcl%qy2X z7XDc%)a51TlE2;Pl15bg#2{JgyLsRDnuN4`UK8E&xw7l`y1Kf%7e4T3TBj~9KGt_E z{P$aZUP!PY$Pb_67)&GQ{9w>vUhL{4$6kQ9fO+92lP micE-kAd{9fJ_nL0Q42H>6tx%zva*3RFahC0Af3bn;sF4n2_829 literal 0 HcmV?d00001