From cc0d63bf03509c1609b7a19a1075ab06ce96fd8d Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 09:57:41 +0530 Subject: [PATCH 01/15] chore: bump storage lib to remove deprecation warnings --- app/controllers/api/migrations.php | 6 +- composer.json | 5 +- composer.lock | 83 +++++++++++-------- .../Modules/Functions/Workers/Screenshots.php | 2 +- .../Modules/Storage/Http/Buckets/Create.php | 2 +- .../Storage/Http/Buckets/Files/Create.php | 6 +- .../Http/Buckets/Files/Download/Get.php | 6 +- .../Http/Buckets/Files/Preview/Get.php | 6 +- .../Storage/Http/Buckets/Files/Push/Get.php | 6 +- .../Storage/Http/Buckets/Files/View/Get.php | 6 +- .../Modules/Storage/Http/Buckets/Update.php | 2 +- src/Appwrite/Platform/Workers/Migrations.php | 2 +- src/Appwrite/Utopia/Response/Model/Bucket.php | 2 +- src/Appwrite/Utopia/Response/Model/File.php | 2 +- 14 files changed, 74 insertions(+), 62 deletions(-) diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 9d5e013cdc..95339feec2 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -11,6 +11,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CompoundUID; use Appwrite\Utopia\Database\Validator\Queries\Migrations; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; @@ -29,9 +32,6 @@ use Utopia\Migration\Sources\Firebase; use Utopia\Migration\Sources\NHost; use Utopia\Migration\Sources\Supabase; use Utopia\Migration\Transfer; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/composer.json b/composer.json index b7d3bc5483..10b202d316 100644 --- a/composer.json +++ b/composer.json @@ -51,6 +51,7 @@ "utopia-php/auth": "0.5.*", "utopia-php/cache": "1.0.*", "utopia-php/cli": "0.22.*", + "utopia-php/compression": "dev-feat-rename-identity-to-none as 0.1.3", "utopia-php/config": "1.*", "utopia-php/console": "0.1.*", "utopia-php/database": "5.*", @@ -65,14 +66,14 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "1.6.*", + "utopia-php/migration": "dev-bump-storage-lib as 1.6.0", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", "utopia-php/span": "1.1.*", "utopia-php/preloader": "0.2.*", "utopia-php/queue": "0.15.*", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.18.*", + "utopia-php/storage": "1.0.*", "utopia-php/system": "0.10.*", "utopia-php/telemetry": "0.2.*", "utopia-php/vcs": "1.*", diff --git a/composer.lock b/composer.lock index ddc2754e98..03ab95303a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fe847eccf6ba73bfca1e08d26e9fc7ab", + "content-hash": "90a81fe7def09b16d16e279e060376a2", "packages": [ { "name": "adhocore/jwt", @@ -3656,20 +3656,20 @@ }, { "name": "utopia-php/compression", - "version": "0.1.3", + "version": "dev-feat-rename-identity-to-none", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "66f093557ba66d98245e562036182016c7dcfe8a" + "reference": "171e37f6fe663991448fcf06acf830f7eb0b05de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/66f093557ba66d98245e562036182016c7dcfe8a", - "reference": "66f093557ba66d98245e562036182016c7dcfe8a", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/171e37f6fe663991448fcf06acf830f7eb0b05de", + "reference": "171e37f6fe663991448fcf06acf830f7eb0b05de", "shasum": "" }, "require": { - "php": ">=8.0" + "php": ">=8.1" }, "require-dev": { "laravel/pint": "1.2.*", @@ -3696,9 +3696,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.3" + "source": "https://github.com/utopia-php/compression/tree/feat-rename-identity-to-none" }, - "time": "2025-01-15T15:15:51+00:00" + "time": "2026-02-17T05:15:15+00:00" }, { "name": "utopia-php/config", @@ -3797,16 +3797,16 @@ }, { "name": "utopia-php/database", - "version": "5.2.0", + "version": "5.2.1", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7" + "reference": "adfdf201144353a1d2ce14bb197ab746079894e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5c89b39de00f2b3126d0fbbdea36786341293df7", - "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7", + "url": "https://api.github.com/repos/utopia-php/database/zipball/adfdf201144353a1d2ce14bb197ab746079894e0", + "reference": "adfdf201144353a1d2ce14bb197ab746079894e0", "shasum": "" }, "require": { @@ -3849,9 +3849,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/5.2.0" + "source": "https://github.com/utopia-php/database/tree/5.2.1" }, - "time": "2026-02-14T09:37:28+00:00" + "time": "2026-02-16T11:01:13+00:00" }, { "name": "utopia-php/detector", @@ -4464,16 +4464,16 @@ }, { "name": "utopia-php/migration", - "version": "1.6.0", + "version": "dev-bump-storage-lib", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849" + "reference": "75c9b9ac8ee4827163711e1aedced2bafb8f265b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", - "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/75c9b9ac8ee4827163711e1aedced2bafb8f265b", + "reference": "75c9b9ac8ee4827163711e1aedced2bafb8f265b", "shasum": "" }, "require": { @@ -4484,7 +4484,7 @@ "php": ">=8.1", "utopia-php/database": "5.*", "utopia-php/dsn": "0.2.*", - "utopia-php/storage": "0.18.*" + "utopia-php/storage": "1.0.*" }, "require-dev": { "ext-pdo": "*", @@ -4513,9 +4513,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.6.0" + "source": "https://github.com/utopia-php/migration/tree/bump-storage-lib" }, - "time": "2026-02-16T07:19:27+00:00" + "time": "2026-02-17T05:24:05+00:00" }, { "name": "utopia-php/mongo", @@ -4953,32 +4953,27 @@ }, { "name": "utopia-php/storage", - "version": "0.18.19", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "9c3f9a471250d22de7d405ee19e23e72b14181ca" + "reference": "f672e8865938e5d1d6dc3bd149ceba4e5582199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/9c3f9a471250d22de7d405ee19e23e72b14181ca", - "reference": "9c3f9a471250d22de7d405ee19e23e72b14181ca", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/f672e8865938e5d1d6dc3bd149ceba4e5582199e", + "reference": "f672e8865938e5d1d6dc3bd149ceba4e5582199e", "shasum": "" }, "require": { - "ext-brotli": "*", "ext-curl": "*", "ext-fileinfo": "*", - "ext-lz4": "*", "ext-simplexml": "*", - "ext-snappy": "*", - "ext-xz": "*", "ext-zlib": "*", - "ext-zstd": "*", "php": ">=8.1", - "utopia-php/framework": "0.*.*", "utopia-php/system": "0.*.*", - "utopia-php/telemetry": "0.2.*" + "utopia-php/telemetry": "0.2.*", + "utopia-php/validators": "0.2.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -5005,9 +5000,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.19" + "source": "https://github.com/utopia-php/storage/tree/1.0.0" }, - "time": "2025-12-17T13:55:20+00:00" + "time": "2026-02-17T04:37:10+00:00" }, { "name": "utopia-php/system", @@ -8888,9 +8883,25 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/compression", + "version": "dev-feat-rename-identity-to-none", + "alias": "0.1.3", + "alias_normalized": "0.1.3.0" + }, + { + "package": "utopia-php/migration", + "version": "dev-bump-storage-lib", + "alias": "1.6.0", + "alias_normalized": "1.6.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "utopia-php/compression": 20, + "utopia-php/migration": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php index f5f7a7974b..485078af88 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php @@ -7,6 +7,7 @@ use Appwrite\Event\Realtime; use Appwrite\Permission; use Appwrite\Role; use Exception; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -16,7 +17,6 @@ use Utopia\Database\Query; use Utopia\Fetch\Client as FetchClient; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php index c78402e5a4..f31d30188a 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -17,7 +18,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php index 6473f1058a..827dbc8dd9 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php @@ -14,6 +14,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -28,9 +31,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\Storage\Storage; use Utopia\Storage\Validator\File; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php index 4a52c87238..042ae76565 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -19,9 +22,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php index a53cbb3a52..63a72fc683 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -23,9 +26,6 @@ use Utopia\Http\Adapter\Swoole\Request; use Utopia\Image\Image; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\HexColor; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php index 60846cb546..c475c53d24 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php @@ -8,6 +8,9 @@ use Appwrite\Extend\Exception; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -16,9 +19,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php index 73e7e58421..cba6c2fa13 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -20,9 +23,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php index 9dd5a29967..e4827a6354 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php @@ -8,13 +8,13 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 94cbcf341c..5e729af98f 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -8,6 +8,7 @@ use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Template\Template; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -39,7 +40,6 @@ use Utopia\Migration\Sources\Supabase; use Utopia\Migration\Transfer; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; diff --git a/src/Appwrite/Utopia/Response/Model/Bucket.php b/src/Appwrite/Utopia/Response/Model/Bucket.php index aece4cf850..d66a27be97 100644 --- a/src/Appwrite/Utopia/Response/Model/Bucket.php +++ b/src/Appwrite/Utopia/Response/Model/Bucket.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use Utopia\Storage\Compression\Compression; +use Utopia\Compression\Compression; class Bucket extends Model { diff --git a/src/Appwrite/Utopia/Response/Model/File.php b/src/Appwrite/Utopia/Response/Model/File.php index c4e36286ea..9b3e6ff618 100644 --- a/src/Appwrite/Utopia/Response/Model/File.php +++ b/src/Appwrite/Utopia/Response/Model/File.php @@ -4,8 +4,8 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Compression\Compression; use Utopia\Database\Document; -use Utopia\Storage\Compression\Compression; class File extends Model { From 8639e70ce93b92c494ac4885348a5e6ab59b63bf Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 11:25:27 +0530 Subject: [PATCH 02/15] use stable --- composer.json | 4 ++-- composer.lock | 46 +++++++++++++++------------------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index 10b202d316..ce55926d23 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/auth": "0.5.*", "utopia-php/cache": "1.0.*", "utopia-php/cli": "0.22.*", - "utopia-php/compression": "dev-feat-rename-identity-to-none as 0.1.3", + "utopia-php/compression": "0.1.*", "utopia-php/config": "1.*", "utopia-php/console": "0.1.*", "utopia-php/database": "5.*", @@ -66,7 +66,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "dev-bump-storage-lib as 1.6.0", + "utopia-php/migration": "1.6.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", "utopia-php/span": "1.1.*", diff --git a/composer.lock b/composer.lock index 03ab95303a..54e8929825 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "90a81fe7def09b16d16e279e060376a2", + "content-hash": "dd9887a50fb434887461a99413007573", "packages": [ { "name": "adhocore/jwt", @@ -3656,16 +3656,16 @@ }, { "name": "utopia-php/compression", - "version": "dev-feat-rename-identity-to-none", + "version": "0.1.4", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "171e37f6fe663991448fcf06acf830f7eb0b05de" + "reference": "68045cb9d714c1259582d2dfd0e76bd34f83e713" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/171e37f6fe663991448fcf06acf830f7eb0b05de", - "reference": "171e37f6fe663991448fcf06acf830f7eb0b05de", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/68045cb9d714c1259582d2dfd0e76bd34f83e713", + "reference": "68045cb9d714c1259582d2dfd0e76bd34f83e713", "shasum": "" }, "require": { @@ -3696,9 +3696,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/feat-rename-identity-to-none" + "source": "https://github.com/utopia-php/compression/tree/0.1.4" }, - "time": "2026-02-17T05:15:15+00:00" + "time": "2026-02-17T05:53:40+00:00" }, { "name": "utopia-php/config", @@ -4464,16 +4464,16 @@ }, { "name": "utopia-php/migration", - "version": "dev-bump-storage-lib", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "75c9b9ac8ee4827163711e1aedced2bafb8f265b" + "reference": "c5c7544d02d2418536d41050794050132f247d62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/75c9b9ac8ee4827163711e1aedced2bafb8f265b", - "reference": "75c9b9ac8ee4827163711e1aedced2bafb8f265b", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/c5c7544d02d2418536d41050794050132f247d62", + "reference": "c5c7544d02d2418536d41050794050132f247d62", "shasum": "" }, "require": { @@ -4513,9 +4513,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/bump-storage-lib" + "source": "https://github.com/utopia-php/migration/tree/1.6.1" }, - "time": "2026-02-17T05:24:05+00:00" + "time": "2026-02-17T05:49:48+00:00" }, { "name": "utopia-php/mongo", @@ -8883,25 +8883,9 @@ "time": "2024-03-07T20:33:40+00:00" } ], - "aliases": [ - { - "package": "utopia-php/compression", - "version": "dev-feat-rename-identity-to-none", - "alias": "0.1.3", - "alias_normalized": "0.1.3.0" - }, - { - "package": "utopia-php/migration", - "version": "dev-bump-storage-lib", - "alias": "1.6.0", - "alias_normalized": "1.6.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/compression": 20, - "utopia-php/migration": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { From bfecddcc9ae5d1da7790479460c72bc6e70bb1b8 Mon Sep 17 00:00:00 2001 From: Atharva Deosthale Date: Tue, 17 Feb 2026 11:38:03 +0530 Subject: [PATCH 03/15] Add status to response model --- src/Appwrite/Utopia/Response/Model/Project.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index c516aab73f..cd33a29685 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -283,6 +283,12 @@ class Project extends Model 'example' => ['vip'], 'array' => true, ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Project status.', + 'default' => 'active', + 'example' => 'active', + ]) ; $services = Config::getParam('services', []); From 0844af9c9d0de16273f71fe651063be395bf71e9 Mon Sep 17 00:00:00 2001 From: Atharva Deosthale Date: Tue, 17 Feb 2026 11:54:22 +0530 Subject: [PATCH 04/15] add tests --- tests/e2e/Services/Projects/ProjectsConsoleClientTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 1d15f10971..e6cb871f52 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -57,6 +57,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertNotEmpty($response['body']['$id']); $this->assertEquals('Project Test', $response['body']['name']); $this->assertEquals($team['body']['$id'], $response['body']['teamId']); + $this->assertEquals(PROJECT_STATUS_ACTIVE, $response['body']['status']); $this->assertArrayHasKey('platforms', $response['body']); $this->assertArrayHasKey('webhooks', $response['body']); $this->assertArrayHasKey('keys', $response['body']); @@ -76,6 +77,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertNotEmpty($response['body']['$id']); $this->assertEquals('Project Test', $response['body']['name']); $this->assertEquals($team['body']['$id'], $response['body']['teamId']); + $this->assertEquals(PROJECT_STATUS_ACTIVE, $response['body']['status']); $this->assertArrayHasKey('platforms', $response['body']); $this->assertArrayHasKey('webhooks', $response['body']); $this->assertArrayHasKey('keys', $response['body']); From 08848e4c5cba07fb67ba926cc52190af6cf525e3 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:31:25 +0000 Subject: [PATCH 05/15] Load static files before Swoole fork for copy-on-write memory sharing Move Files::load() from EVENT_WORKER_START (per-worker) to before the Swoole server starts. This allows forked workers to share the loaded file data via OS copy-on-write instead of each worker allocating its own copy (~22MB per worker). Co-Authored-By: Claude Opus 4.6 --- app/http.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/http.php b/app/http.php index 1d7949da86..3414d3ba9d 100644 --- a/app/http.php +++ b/app/http.php @@ -37,7 +37,8 @@ use Utopia\System\System; const DOMAIN_SYNC_TIMER = 30; // 30 seconds -$files = null; +$files = new Files(); +$files->load(__DIR__ . '/../public'); $domains = new Table(1_000_000); // 1 million rows $domains->column('value', Table::TYPE_INT, 1); @@ -164,11 +165,7 @@ $http Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background ]); -$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) use (&$files) { - if (!$server->taskworker) { - $files = new Files(); - $files->load(__DIR__ . '/../public'); - } +$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) { }); $http->on(Constant::EVENT_WORKER_STOP, function ($server, $workerId) { @@ -452,7 +449,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $tot }); }); -$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, &$files) { +$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, $files) { Span::init('http.request'); Http::setResource('swooleRequest', fn () => $swooleRequest); @@ -463,7 +460,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool Span::add('http.method', $request->getMethod()); - if ($files instanceof Files && $files->isFileLoaded($request->getURI())) { + if ($files->isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 45); // 45 days cache $response From b9258df955e3f65f1357c44eae39f3304c901c21 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:19:53 +0000 Subject: [PATCH 06/15] Replace per-worker Config cache with shared Swoole Table for hostname cert tracking The hostname certificate cache was using Config::getParam/setParam which stores a plain PHP array local to each worker. This meant every worker independently hit the DB for the same hostnames. Replace with a Swoole Table shared across all workers via shared memory. Co-Authored-By: Claude Opus 4.6 --- app/controllers/general.php | 13 +++++-------- app/http.php | 4 ++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 2d3bde69d6..1efb9ab4b7 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1073,21 +1073,19 @@ Http::init() ->inject('queueForCertificates') ->inject('platform') ->inject('authorization') - ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) { + ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) use ($hostnames) { $hostname = $request->getHostname(); - $cache = Config::getParam('hostnames', []); $platformHostnames = $platform['hostnames'] ?? []; // 1. Cache hit - if (array_key_exists($hostname, $cache)) { + if ($hostnames->exists(md5($hostname))) { return; } // 2. Domain validation $domain = new Domain(!empty($hostname) ? $hostname : ''); if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) { - $cache[$domain->get()] = false; - Config::setParam('hostnames', $cache); + $hostnames->set(md5($domain->get()), ['value' => 0]); return; } @@ -1101,7 +1099,7 @@ Http::init() } // 4. Check/create rule (requires DB access) - $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, &$cache) { + $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, $hostnames) { try { // TODO: (@Meldiron) Remove after 1.7.x migration $isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5'; @@ -1164,8 +1162,7 @@ Http::init() } catch (Duplicate $e) { Console::info('Certificate already exists'); } finally { - $cache[$domain->get()] = true; - Config::setParam('hostnames', $cache); + $hostnames->set(md5($domain->get()), ['value' => 1]); } }); }); diff --git a/app/http.php b/app/http.php index 1d7949da86..9012e821ae 100644 --- a/app/http.php +++ b/app/http.php @@ -43,6 +43,10 @@ $domains = new Table(1_000_000); // 1 million rows $domains->column('value', Table::TYPE_INT, 1); $domains->create(); +$hostnames = new Table(100_000); +$hostnames->column('value', Table::TYPE_INT, 1); +$hostnames->create(); + $http = new Server( host: "0.0.0.0", port: System::getEnv('PORT', 80), From 56cbcba2b17de0e1e5561ae180e30340e9d88451 Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:37:28 +0000 Subject: [PATCH 07/15] Resize domains Swoole Table from 1M to 100K rows Co-Authored-By: Claude Opus 4.6 --- app/http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/http.php b/app/http.php index 7b12cfe1b4..be8bb05122 100644 --- a/app/http.php +++ b/app/http.php @@ -40,7 +40,7 @@ const DOMAIN_SYNC_TIMER = 30; // 30 seconds $files = new Files(); $files->load(__DIR__ . '/../public'); -$domains = new Table(1_000_000); // 1 million rows +$domains = new Table(100_000); $domains->column('value', Table::TYPE_INT, 1); $domains->create(); From 190b866da19adf2db06c9ef115aeada8d5c5e5ed Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 18:20:35 +0530 Subject: [PATCH 08/15] Bump Web SDK to 22.3.1 and React Native SDK to 0.23.1 Add patch changelog entries for removing bignumber.js direct dependency to fix bundler conflict with Next.js Turbopack, and adding upsert method to Realtime Channels helper class. --- app/config/sdks.php | 4 +- composer.lock | 48 +++++++++---------- .../databases/create-longtext-attribute.md | 3 +- .../databases/create-mediumtext-attribute.md | 3 +- .../databases/create-text-attribute.md | 3 +- .../databases/create-varchar-attribute.md | 3 +- .../tablesdb/create-longtext-column.md | 3 +- .../tablesdb/create-mediumtext-column.md | 3 +- .../examples/tablesdb/create-text-column.md | 3 +- .../tablesdb/create-varchar-column.md | 3 +- docs/sdks/react-native/CHANGELOG.md | 5 ++ docs/sdks/web/CHANGELOG.md | 5 ++ 12 files changed, 52 insertions(+), 34 deletions(-) diff --git a/app/config/sdks.php b/app/config/sdks.php index a3088b247b..1bf2725785 100644 --- a/app/config/sdks.php +++ b/app/config/sdks.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '22.1.0', + 'version' => '22.3.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -140,7 +140,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.21.0', + 'version' => '0.23.1', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, diff --git a/composer.lock b/composer.lock index 54e8929825..5a6958c241 100644 --- a/composer.lock +++ b/composer.lock @@ -161,16 +161,16 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.19.3", + "version": "0.19.4", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e" + "reference": "eea9d1b3ca2540eab623b419c8afde09ef406c0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e", - "reference": "bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/eea9d1b3ca2540eab623b419c8afde09ef406c0b", + "reference": "eea9d1b3ca2540eab623b419c8afde09ef406c0b", "shasum": "" }, "require": { @@ -210,9 +210,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.19.3" + "source": "https://github.com/appwrite/runtimes/tree/0.19.4" }, - "time": "2026-02-06T10:08:18+00:00" + "time": "2026-02-17T10:04:39+00:00" }, { "name": "brick/math", @@ -3797,16 +3797,16 @@ }, { "name": "utopia-php/database", - "version": "5.2.1", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "adfdf201144353a1d2ce14bb197ab746079894e0" + "reference": "5e49f32ebb6e40a78209231564c40e5068810429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/adfdf201144353a1d2ce14bb197ab746079894e0", - "reference": "adfdf201144353a1d2ce14bb197ab746079894e0", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5e49f32ebb6e40a78209231564c40e5068810429", + "reference": "5e49f32ebb6e40a78209231564c40e5068810429", "shasum": "" }, "require": { @@ -3849,9 +3849,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/5.2.1" + "source": "https://github.com/utopia-php/database/tree/5.3.0" }, - "time": "2026-02-16T11:01:13+00:00" + "time": "2026-02-17T11:50:01+00:00" }, { "name": "utopia-php/detector", @@ -3948,16 +3948,16 @@ }, { "name": "utopia-php/dns", - "version": "1.6.2", + "version": "1.6.3", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab" + "reference": "c46a13c42bc19365985264a6fdaaaf7d8e736d3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/98c70520213a41e2fe1867e5b110273c06bf1cab", - "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/c46a13c42bc19365985264a6fdaaaf7d8e736d3e", + "reference": "c46a13c42bc19365985264a6fdaaaf7d8e736d3e", "shasum": "" }, "require": { @@ -3999,9 +3999,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.6.2" + "source": "https://github.com/utopia-php/dns/tree/1.6.3" }, - "time": "2026-02-13T12:29:08+00:00" + "time": "2026-02-17T10:45:46+00:00" }, { "name": "utopia-php/domains", @@ -5385,16 +5385,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "74de906ea5051030c5299a5d4aa74d963a531130" + "reference": "5a8a304e404c84539f3ac6562c736f8c7ef2bef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/74de906ea5051030c5299a5d4aa74d963a531130", - "reference": "74de906ea5051030c5299a5d4aa74d963a531130", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/5a8a304e404c84539f3ac6562c736f8c7ef2bef2", + "reference": "5a8a304e404c84539f3ac6562c736f8c7ef2bef2", "shasum": "" }, "require": { @@ -5430,9 +5430,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.9.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.9.3" }, - "time": "2026-02-16T06:59:54+00:00" + "time": "2026-02-17T11:48:05+00:00" }, { "name": "doctrine/annotations", diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md index 2f777f513b..2a45350168 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createLongtextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md index 4a07863a87..6d47b00f2b 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createMediumtextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md index 8e2a1c979b..d883c36b89 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createTextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md index f5d61db290..9b3d2a4924 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md @@ -14,7 +14,8 @@ const result = await databases.createVarcharAttribute({ size: 1, required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md index d3b25f3046..89603a4780 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createLongtextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md index a0ec9f2192..aef3b40476 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createMediumtextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md index 65a79782ee..763e2df801 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createTextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md index a5d0978ddf..eee225d886 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md @@ -14,7 +14,8 @@ const result = await tablesDB.createVarcharColumn({ size: 1, required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index d111ee5a20..332b27ada4 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log +## 0.23.1 + +* Add `upsert` method to Realtime `Channels` helper class +* Fix `bignumber.js` bundler conflict by removing direct dependency in favor of transitive dependency from `json-bigint` + ## 0.21.0 * Add `queries` parameter to `client.subscribe()` for filtering Realtime events diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index 7f24cb7c38..a91f186fc4 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 22.3.1 + +* Add `upsert` method to Realtime `Channels` helper class +* Fix `bignumber.js` bundler conflict with Next.js Turbopack by removing direct dependency in favor of transitive dependency from `json-bigint` + ## 22.1.0 * Add `queries` parameter to `Realtime.subscribe()` and `client.subscribe()` for server-side query filtering From 634d05ae7693d8fefcb4691f57a3b66fc5d3f5fa Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 18:24:57 +0530 Subject: [PATCH 09/15] update cli entry too --- docs/sdks/cli/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 5aa8ee4cdc..9d83abe9c1 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -4,6 +4,9 @@ * Add `--queries` option to `list-keys` command * Add `--key-id` option to `create-key` command +* Add `encrypt` param support to new string type attributes +* Fix `bignumber.js` bundler conflict by removing direct dependency in favor of transitive dependency from `json-bigint` +* Fix missing region in console failUrl for failed deployments ## 13.3.2 From b83e3ef36ea66d2217c52abb335e6c851e3351bb Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:15:25 +0000 Subject: [PATCH 10/15] Make Swoole Tables injectable Http resources Register both `domains` and `hostnames` tables as Http resources so they can be injected via the framework instead of relying on closures or globals. Co-Authored-By: Claude Opus 4.6 --- app/controllers/general.php | 4 +++- app/http.php | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 1efb9ab4b7..cc38a2ef44 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -34,6 +34,7 @@ use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; use Swoole\Http\Request as SwooleRequest; +use Swoole\Table; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -1073,7 +1074,8 @@ Http::init() ->inject('queueForCertificates') ->inject('platform') ->inject('authorization') - ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) use ($hostnames) { + ->inject('hostnames') + ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization, Table $hostnames) { $hostname = $request->getHostname(); $platformHostnames = $platform['hostnames'] ?? []; diff --git a/app/http.php b/app/http.php index be8bb05122..e921b154de 100644 --- a/app/http.php +++ b/app/http.php @@ -48,6 +48,9 @@ $hostnames = new Table(100_000); $hostnames->column('value', Table::TYPE_INT, 1); $hostnames->create(); +Http::setResource('domains', fn () => $domains); +Http::setResource('hostnames', fn () => $hostnames); + $http = new Server( host: "0.0.0.0", port: System::getEnv('PORT', 80), @@ -583,7 +586,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool }); // Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory -$http->on(Constant::EVENT_TASK, function () use ($register, $domains) { +$http->on(Constant::EVENT_TASK, function () use ($register) { $lastSyncUpdate = null; $pools = $register->get('pools'); Http::setResource('pools', fn () => $pools); @@ -592,6 +595,9 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) { /** @var Utopia\Database\Database $dbForPlatform */ $dbForPlatform = $app->getResource('dbForPlatform'); + /** @var Table $domains */ + $domains = $app->getResource('domains'); + Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate, $app) { try { $time = DateTime::now(); From f1501d139bbf9a7c153d31b1223e53f8aa7fc76f Mon Sep 17 00:00:00 2001 From: loks0n <22452787+loks0n@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:22:07 +0000 Subject: [PATCH 11/15] Rename Swoole Tables: domains -> riskyDomains, hostnames -> certifiedDomains Co-Authored-By: Claude Opus 4.6 --- app/controllers/general.php | 12 ++++++------ app/http.php | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index cc38a2ef44..7fdbeb1855 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -1074,20 +1074,20 @@ Http::init() ->inject('queueForCertificates') ->inject('platform') ->inject('authorization') - ->inject('hostnames') - ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization, Table $hostnames) { + ->inject('certifiedDomains') + ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization, Table $certifiedDomains) { $hostname = $request->getHostname(); $platformHostnames = $platform['hostnames'] ?? []; // 1. Cache hit - if ($hostnames->exists(md5($hostname))) { + if ($certifiedDomains->exists(md5($hostname))) { return; } // 2. Domain validation $domain = new Domain(!empty($hostname) ? $hostname : ''); if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) { - $hostnames->set(md5($domain->get()), ['value' => 0]); + $certifiedDomains->set(md5($domain->get()), ['value' => 0]); return; } @@ -1101,7 +1101,7 @@ Http::init() } // 4. Check/create rule (requires DB access) - $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, $hostnames) { + $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, $certifiedDomains) { try { // TODO: (@Meldiron) Remove after 1.7.x migration $isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5'; @@ -1164,7 +1164,7 @@ Http::init() } catch (Duplicate $e) { Console::info('Certificate already exists'); } finally { - $hostnames->set(md5($domain->get()), ['value' => 1]); + $certifiedDomains->set(md5($domain->get()), ['value' => 1]); } }); }); diff --git a/app/http.php b/app/http.php index e921b154de..d771796a57 100644 --- a/app/http.php +++ b/app/http.php @@ -40,16 +40,16 @@ const DOMAIN_SYNC_TIMER = 30; // 30 seconds $files = new Files(); $files->load(__DIR__ . '/../public'); -$domains = new Table(100_000); -$domains->column('value', Table::TYPE_INT, 1); -$domains->create(); +$riskyDomains = new Table(100_000); +$riskyDomains->column('value', Table::TYPE_INT, 1); +$riskyDomains->create(); -$hostnames = new Table(100_000); -$hostnames->column('value', Table::TYPE_INT, 1); -$hostnames->create(); +$certifiedDomains = new Table(100_000); +$certifiedDomains->column('value', Table::TYPE_INT, 1); +$certifiedDomains->create(); -Http::setResource('domains', fn () => $domains); -Http::setResource('hostnames', fn () => $hostnames); +Http::setResource('riskyDomains', fn () => $riskyDomains); +Http::setResource('certifiedDomains', fn () => $certifiedDomains); $http = new Server( host: "0.0.0.0", @@ -78,7 +78,7 @@ $totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intva function dispatch(Server $server, int $fd, int $type, $data = null): int { $resolveWorkerId = function (Server $server, $data = null) { - global $totalWorkers, $domains; + global $totalWorkers, $riskyDomains; // If data is not set we can send request to any worker // first we try to pick idle worker, if not we randomly pick a worker @@ -110,7 +110,7 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int $risky = false; if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) { $risky = true; - } elseif ($domains->get(md5($domain), 'value') === 1) { + } elseif ($riskyDomains->get(md5($domain), 'value') === 1) { // executions request coming from custom domain $risky = true; } else { @@ -595,10 +595,10 @@ $http->on(Constant::EVENT_TASK, function () use ($register) { /** @var Utopia\Database\Database $dbForPlatform */ $dbForPlatform = $app->getResource('dbForPlatform'); - /** @var Table $domains */ - $domains = $app->getResource('domains'); + /** @var Table $riskyDomains */ + $riskyDomains = $app->getResource('riskyDomains'); - Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate, $app) { + Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $riskyDomains, &$lastSyncUpdate, $app) { try { $time = DateTime::now(); $limit = 1000; @@ -651,7 +651,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register) { continue; } - $domains->set(md5($domain), ['value' => 1]); + $riskyDomains->set(md5($domain), ['value' => 1]); } $latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null; } From 6850ae9be69a4954664deac2897cbd10a8ad17fc Mon Sep 17 00:00:00 2001 From: Hemachandar <132386067+hmacr@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:36:42 +0530 Subject: [PATCH 12/15] Add array support for DNS A records (#11343) * Add array support for DNS A records * feedback --- .../Modules/Console/Http/Variables/Get.php | 5 +++-- src/Appwrite/Platform/Modules/Proxy/Action.php | 16 ++++++++++------ src/Appwrite/Platform/Tasks/Doctor.php | 7 ++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php index af83a3efd3..40b3692ad9 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php @@ -56,7 +56,8 @@ class Get extends Action $isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest(); $validator = new IP(IP::V4); - $isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A'))); + $targetA = \explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', ''))[0]; + $isAValid = !empty($targetA) && ($validator->isValid($targetA)); $validator = new IP(IP::V6); $isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA')); @@ -74,7 +75,7 @@ class Get extends Action $variables = new Document([ '_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'), '_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'), - '_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'), + '_APP_DOMAIN_TARGET_A' => $targetA, '_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"', '_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'), '_APP_COMPUTE_BUILD_TIMEOUT' => +System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT'), diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index 62d6efbfa3..a5b0500aa0 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -178,13 +178,17 @@ class Action extends PlatformAction } // Ensure at least one of CNAME/A/AAAA record points to our servers properly - $targetA = System::getEnv('_APP_DOMAIN_TARGET_A', ''); - if ((new IP(IP::V4))->isValid($targetA)) { - $validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers); - $validators[] = $validator; + foreach (\explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', '')) as $targetA) { + if (empty($targetA)) { + continue; + } + if ((new IP(IP::V4))->isValid($targetA)) { + $validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers); + $validators[] = $validator; - if (\is_null($mainValidator)) { - $mainValidator = $validator; + if (\is_null($mainValidator)) { + $mainValidator = $validator; + } } } diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 8ac1e1121b..9a9c2fdf73 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -63,10 +63,11 @@ class Doctor extends Action } $ipv4 = new IP(IP::V4); - if (!$ipv4->isValid(System::getEnv('_APP_DOMAIN_TARGET_A'))) { - Console::log('🔴 A record target is not valid (' . System::getEnv('_APP_DOMAIN_TARGET_A') . ')'); + $targetA = \explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', ''))[0]; + if (!$ipv4->isValid($targetA)) { + Console::log('🔴 A record target is not valid (' . $targetA . ')'); } else { - Console::log('🟢 A record target is valid (' . System::getEnv('_APP_DOMAIN_TARGET_A') . ')'); + Console::log('🟢 A record target is valid (' . $targetA . ')'); } $ipv6 = new IP(IP::V6); From a1fb4922928408a6bad17da1159d83711d27e345 Mon Sep 17 00:00:00 2001 From: Hemachandar <132386067+hmacr@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:00:26 +0530 Subject: [PATCH 13/15] Change CNAME for functions custom domains (#11348) * Change CNAME for functions custom domains * fix test --- src/Appwrite/Platform/Modules/Proxy/Action.php | 12 ++---------- tests/resources/coredns/Corefile | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index a5b0500aa0..30ad140530 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -130,15 +130,7 @@ class Action extends PlatformAction $resourceType = $rule->getAttribute('deploymentResourceType', ''); // Ensures different target based on rule's type, as configured by env variables - if ($resourceType === 'function') { - // For example: fra.appwrite.run - foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) as $targetCNAME) { - if (empty($targetCNAME)) { - continue; - } - $targetCNAMEs[] = new Domain($targetCNAME); - } - } elseif ($resourceType === 'site') { + if ($resourceType === 'site') { // For example: appwrite.network foreach (\explode(',', System::getEnv('_APP_DOMAIN_SITES', '')) as $targetCNAME) { if (empty($targetCNAME)) { @@ -146,7 +138,7 @@ class Action extends PlatformAction } $targetCNAMEs[] = new Domain($targetCNAME); } - } elseif ($ruleType === 'api') { + } elseif ($resourceType === 'function' || $ruleType === 'api') { // For example: fra.cloud.appwrite.io $targetCNAMEs[] = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); } elseif ($ruleType === 'redirect') { diff --git a/tests/resources/coredns/Corefile b/tests/resources/coredns/Corefile index b156ba336d..7b312e8498 100644 --- a/tests/resources/coredns/Corefile +++ b/tests/resources/coredns/Corefile @@ -19,7 +19,7 @@ webapp.com { template IN CNAME { match "^stage-function\.webapp\.com\.$" - answer "{{ .Name }} 60 IN CNAME functions.localhost." + answer "{{ .Name }} 60 IN CNAME cname.localhost." fallthrough } From b9d7788d386f7b9a30f61fc7bfe3ab5b3c7213a4 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 22:01:40 +0530 Subject: [PATCH 14/15] feat: add git push and PR support to specs task, add examples-only mode to SDKs task - Specs task now supports --git, --message, and --branch params to push generated specs and SDK examples to the appwrite/specs repo and create a PR - SDKs task now supports --mode=examples to only generate and copy examples without git push or releases - Specs task invokes SDKs in examples mode to regenerate examples before including them in the specs PR --- src/Appwrite/Platform/Tasks/SDKs.php | 15 ++- src/Appwrite/Platform/Tasks/Specs.php | 126 +++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index f45435757b..9020cd3208 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -77,11 +77,13 @@ class SDKs extends Action ->param('release', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we create releases?', optional: true) ->param('commit', null, new Nullable(new WhiteList(['yes', 'no'])), 'Actually create releases (yes) or dry-run (no)?', optional: true) ->param('sdks', null, new Nullable(new Text(256)), 'Selected SDKs', optional: true) + ->param('mode', 'full', new WhiteList(['full', 'examples']), 'Generation mode: full (default) or examples (only generate and copy examples)', optional: true) ->callback($this->action(...)); } - public function action(?string $platform, ?string $sdk, ?string $version, ?string $git, ?string $message, ?string $release, ?string $commit, ?string $sdks): void + public function action(?string $platform, ?string $sdk, ?string $version, ?string $git, ?string $message, ?string $release, ?string $commit, ?string $sdks, string $mode): void { + $examplesOnly = ($mode === 'examples'); $selectedPlatform = $platform; $selectedSDK = $sdk; @@ -99,7 +101,7 @@ class SDKs extends Action $createRelease = ($release === 'yes'); $commitRelease = ($commit === 'yes'); - if (!$createRelease) { + if (!$createRelease && !$examplesOnly) { $git ??= Console::confirm('Should we use git push? (yes/no)'); $git = ($git === 'yes'); @@ -108,6 +110,9 @@ class SDKs extends Action if ($git) { $message ??= Console::confirm('Please enter your commit message:'); } + } elseif ($examplesOnly) { + $git = false; + $prUrls = []; } if (!\in_array($version, [ @@ -293,7 +298,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND throw new \Exception('Language "' . $language['key'] . '" not supported'); } - if ($createRelease) { + if ($createRelease && !$examplesOnly) { $releaseVersion = $language['version']; $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; @@ -403,7 +408,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND continue; } - Console::info("Generating {$language['name']} SDK..."); + Console::info($examplesOnly + ? "Generating examples for {$language['name']} SDK..." + : "Generating {$language['name']} SDK..."); $sdk = new SDK($config, new Swagger2($spec)); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 02d83e6d48..adc00d83df 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -23,6 +23,7 @@ use Utopia\Http\Request as UtopiaRequest; use Utopia\Http\Response as UtopiaResponse; use Utopia\Platform\Action; use Utopia\System\System; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -34,6 +35,9 @@ class Specs extends Action ->desc('Generate Appwrite API specifications') ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) + ->param('git', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we push to the specs repo?', optional: true) + ->param('message', null, new Nullable(new Text(256)), 'Commit Message', optional: true) + ->param('branch', null, new Nullable(new Text(256)), 'Target branch for PR (defaults to main)', optional: true) ->callback($this->action(...)); } @@ -230,8 +234,20 @@ class Specs extends Action return $sdkPlatforms; } - public function action(string $version, string $mode): void + public function action(string $version, string $mode, ?string $git, ?string $message, ?string $branch): void { + if (\is_null($git)) { + $git = Console::confirm('Should we push specs to the appwrite/specs repo? (yes/no)'); + } + + if ($git === 'yes' && \is_null($message)) { + $message = Console::confirm('Please enter your commit message:'); + } + + if (\is_null($branch)) { + $branch = 'main'; + } + $appRoutes = Http::getRoutes(); /** @var AppwriteResponse $response */ @@ -249,6 +265,8 @@ class Specs extends Action $authCounts = $this->getAuthCounts(); $keys = $this->getKeys(); + $generatedFiles = []; + foreach ($platforms as $platform) { $routes = []; $models = []; @@ -371,6 +389,7 @@ class Specs extends Action throw new Exception('Failed to save mocks spec file: ' . $path); } + $generatedFiles[] = realpath($path); Console::success('Saved mocks spec file: ' . realpath($path)); continue; @@ -382,8 +401,113 @@ class Specs extends Action throw new Exception('Failed to save spec file: ' . $path); } + $generatedFiles[] = realpath($path); Console::success('Saved spec file: ' . realpath($path)); } } + + if ($git === 'yes') { + $gitUrl = 'git@github.com:appwrite/specs.git'; + $gitRepoName = 'appwrite/specs'; + $gitBranch = 'feat-' . $version . '-specs'; + $repoBranch = $branch; + $target = \realpath(__DIR__ . '/../../../../app') . '/sdks/git/specs/'; + $examplesDir = \realpath(__DIR__ . '/../../../..') . '/docs/examples/' . $version; + + Console::info("Cloning {$gitRepoName} into {$target}..."); + + \exec('rm -rf ' . $target . ' && \ + mkdir -p ' . $target . ' && \ + cd ' . $target . ' && \ + git init && \ + git config core.ignorecase false && \ + git config pull.rebase false && \ + git remote add origin ' . $gitUrl . ' && \ + git fetch origin && \ + (git checkout -f ' . $repoBranch . ' 2>/dev/null || git checkout -b ' . $repoBranch . ') && \ + git pull origin ' . $repoBranch . ' && \ + (git checkout -f ' . $gitBranch . ' 2>/dev/null || git checkout -b ' . $gitBranch . ') && \ + (git fetch origin ' . $gitBranch . ' 2>/dev/null || git push -u origin ' . $gitBranch . ') && \ + git reset --hard origin/' . $gitBranch . ' 2>/dev/null || true + '); + + // Copy generated spec files into specs/{version}/ subdirectory + $specsSubDir = $mocks ? 'mocks' : $version; + \exec('mkdir -p ' . \escapeshellarg("{$target}/specs/{$specsSubDir}")); + foreach ($generatedFiles as $file) { + $fileName = \basename($file); + \exec('cp ' . \escapeshellarg($file) . ' ' . \escapeshellarg("{$target}/specs/{$specsSubDir}/{$fileName}")); + Console::success("Copied spec file to repo: specs/{$specsSubDir}/{$fileName}"); + } + + // Regenerate SDK examples for this version + Console::info("Regenerating SDK examples for version {$version}..."); + $sdksCommand = 'php ' . \escapeshellarg(\realpath(__DIR__ . '/../../../../app') . '/cli.php') + . ' sdks --platform=* --sdk=* --version=' . \escapeshellarg($version) + . ' --git=no --mode=examples 2>&1'; + \exec($sdksCommand, $sdksOutput, $sdksReturnCode); + + if ($sdksReturnCode !== 0) { + Console::warning("SDK examples generation returned non-zero exit code: {$sdksReturnCode}"); + Console::warning(\implode("\n", $sdksOutput)); + } else { + Console::success("Regenerated SDK examples for version {$version}"); + } + + // Copy SDK examples for this version + if (\is_dir($examplesDir)) { + \exec('mkdir -p ' . \escapeshellarg("{$target}/examples/{$version}") . ' && \ + cp -r ' . \escapeshellarg($examplesDir) . '/. ' . \escapeshellarg("{$target}/examples/{$version}/")); + Console::success("Copied SDK examples for version {$version} to repo: examples/{$version}/"); + } else { + Console::warning("No SDK examples found at: {$examplesDir}. Skipping examples copy."); + } + + // Git add, commit, and push + \exec('cd ' . $target . ' && \ + git add -A && \ + git commit -m "' . \addslashes($message) . '" && \ + git push -u origin ' . $gitBranch . ' + '); + + Console::success("Pushed specs to {$gitRepoName} on branch {$gitBranch}"); + + // Create or update PR + $prTitle = "feat: API specs update for version {$version}"; + $prBody = "This PR contains API specification updates and SDK examples for version {$version}."; + + $prCommand = 'cd ' . $target . ' && \ + gh pr create \ + --repo "' . $gitRepoName . '" \ + --title "' . $prTitle . '" \ + --body "' . $prBody . '" \ + --base "' . $repoBranch . '" \ + --head "' . $gitBranch . '" \ + 2>&1'; + + $prUrl = ''; + \exec($prCommand, $prOutput); + $prOutput = \implode("\n", $prOutput); + + if (\str_contains($prOutput, 'already exists')) { + Console::warning("PR already exists for branch {$gitBranch}"); + // Try to get the existing PR URL + \exec('cd ' . $target . ' && gh pr view --repo "' . $gitRepoName . '" --json url -q .url 2>&1', $existingPrOutput); + $prUrl = \trim(\implode("\n", $existingPrOutput)); + } else { + $prUrl = \trim($prOutput); + } + + // Clean up temp directory + \exec('chmod -R u+w ' . $target . ' && rm -rf ' . $target); + Console::success("Removed temp directory '{$target}'"); + + if (!empty($prUrl)) { + Console::log(''); + Console::log('Pull Request Summary'); + Console::log("Specs PR: {$prUrl}"); + Console::log(''); + } + } } } From 764f4dc563ba496e3a29baf709b42f0516a6a1ae Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 17 Feb 2026 23:10:30 +0530 Subject: [PATCH 15/15] ci: add workflow_dispatch GitHub Action for specs generation Allows manually triggering spec generation from the Actions tab with version selection and optional push to appwrite/specs repo. --- .github/workflows/specs.yml | 113 ++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 .github/workflows/specs.yml diff --git a/.github/workflows/specs.yml b/.github/workflows/specs.yml new file mode 100644 index 0000000000..6f377354d5 --- /dev/null +++ b/.github/workflows/specs.yml @@ -0,0 +1,113 @@ +name: "Generate Specs" + +env: + COMPOSE_FILE: docker-compose.yml + +on: + workflow_dispatch: + inputs: + version: + type: choice + description: "Appwrite version to generate specs for" + required: true + options: + - "1.8.x" + - "1.7.x" + - "1.6.x" + - "1.5.x" + - "latest" + push: + type: boolean + description: "Push specs to appwrite/specs repo and create PR" + default: true + message: + type: string + description: "Commit message for the specs PR" + default: "chore: update API specs and SDK examples" + +jobs: + generate: + name: Generate Specs + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Build and start Appwrite + run: | + docker compose build + docker compose up -d + + - name: Generate specs + run: | + docker compose exec appwrite specs --version=${{ inputs.version }} --mode=normal --git=no + + - name: Generate SDK examples + if: inputs.push + run: | + docker compose exec appwrite sdks --platform=* --sdk=* --version=${{ inputs.version }} --git=no --mode=examples + sudo chown -R $USER:$USER ./docs/examples/ + + - name: Push to appwrite/specs and create PR + if: inputs.push + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ inputs.version }}" + MESSAGE="${{ inputs.message }}" + GIT_BRANCH="feat-${VERSION}-specs" + SPECS_DIR="./app/config/specs" + EXAMPLES_DIR="./docs/examples/${VERSION}" + TARGET="/tmp/specs-repo" + + sudo chown -R $USER:$USER "${SPECS_DIR}" + + # Clone the specs repo + git clone "https://x-access-token:${GH_TOKEN}@github.com/appwrite/specs.git" "${TARGET}" + cd "${TARGET}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Create or checkout the feature branch + git checkout -b "${GIT_BRANCH}" || git checkout "${GIT_BRANCH}" + + # Copy spec files + mkdir -p "specs/${VERSION}" + cp ${GITHUB_WORKSPACE}/${SPECS_DIR}/*${VERSION}*.json "specs/${VERSION}/" 2>/dev/null || true + + # Copy latest specs if version is latest + if [ "${VERSION}" = "latest" ]; then + cp ${GITHUB_WORKSPACE}/${SPECS_DIR}/*latest*.json "specs/latest/" 2>/dev/null || true + fi + + # Copy SDK examples + if [ -d "${GITHUB_WORKSPACE}/docs/examples/${VERSION}" ]; then + mkdir -p "examples/${VERSION}" + cp -r "${GITHUB_WORKSPACE}/docs/examples/${VERSION}/." "examples/${VERSION}/" + fi + + # Commit and push + git add -A + git diff --cached --quiet && echo "No changes to commit" && exit 0 + git commit -m "${MESSAGE}" + git push -u origin "${GIT_BRANCH}" --force + + # Create or update PR + EXISTING_PR=$(gh pr list --repo appwrite/specs --head "${GIT_BRANCH}" --json number --jq '.[0].number' 2>/dev/null || true) + if [ -n "${EXISTING_PR}" ]; then + echo "PR #${EXISTING_PR} already exists, updated branch" + echo "https://github.com/appwrite/specs/pull/${EXISTING_PR}" + else + gh pr create \ + --repo "appwrite/specs" \ + --title "feat: API specs update for version ${VERSION}" \ + --body "This PR contains API specification updates and SDK examples for version ${VERSION}." \ + --base "main" \ + --head "${GIT_BRANCH}" + fi + + - name: Stop containers + if: always() + run: docker compose down