mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '1.9.x' into feat/cache-circuit-breaker
This commit is contained in:
@@ -320,6 +320,26 @@ return [
|
||||
'repoBranch' => 'main',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/claude-plugin/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
'key' => 'codex-plugin',
|
||||
'name' => 'CodexPlugin',
|
||||
'version' => '0.1.0',
|
||||
'url' => 'https://github.com/appwrite/codex-plugin.git',
|
||||
'enabled' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'spec' => 'static',
|
||||
'family' => APP_SDK_PLATFORM_STATIC,
|
||||
'prism' => 'codex-plugin',
|
||||
'source' => \realpath(__DIR__ . '/../sdks/static-codex-plugin'),
|
||||
'gitUrl' => 'git@github.com:appwrite/codex-plugin.git',
|
||||
'gitRepoName' => 'codex-plugin',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'repoBranch' => 'main',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/codex-plugin/CHANGELOG.md'),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
Generated
+14
-32
@@ -4603,16 +4603,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "1.10.1",
|
||||
"version": "1.10.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827"
|
||||
"reference": "211d01b90ccab9729029151c6c61f543bd755c2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/759d6d61b327313cbeeeb4ea0c3e2459164b4827",
|
||||
"reference": "759d6d61b327313cbeeeb4ea0c3e2459164b4827",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/211d01b90ccab9729029151c6c61f543bd755c2e",
|
||||
"reference": "211d01b90ccab9729029151c6c61f543bd755c2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4638,25 +4638,7 @@
|
||||
"Utopia\\Migration\\": "src/Migration"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Utopia\\Tests\\": "tests/Migration"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"./vendor/bin/phpunit"
|
||||
],
|
||||
"lint": [
|
||||
"./vendor/bin/pint --test"
|
||||
],
|
||||
"format": [
|
||||
"./vendor/bin/pint"
|
||||
],
|
||||
"check": [
|
||||
"./vendor/bin/phpstan analyse --level 3 src tests --memory-limit 2G"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
@@ -4669,10 +4651,10 @@
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.10.1",
|
||||
"issues": "https://github.com/utopia-php/migration/issues"
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/1.10.2"
|
||||
},
|
||||
"time": "2026-05-07T07:23:57+00:00"
|
||||
"time": "2026-05-08T06:25:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -5556,16 +5538,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.27.5",
|
||||
"version": "1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "9faa38b48d422f3da764a719712905c83b3922cb"
|
||||
"reference": "e363fffd220172c5f1a5032038fa3fdafeeb2dfb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9faa38b48d422f3da764a719712905c83b3922cb",
|
||||
"reference": "9faa38b48d422f3da764a719712905c83b3922cb",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e363fffd220172c5f1a5032038fa3fdafeeb2dfb",
|
||||
"reference": "e363fffd220172c5f1a5032038fa3fdafeeb2dfb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5601,9 +5583,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.27.5"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.28.0"
|
||||
},
|
||||
"time": "2026-05-05T12:09:40+00:00"
|
||||
"time": "2026-05-08T03:37:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
|
||||
@@ -72,7 +72,7 @@ class Google extends OAuth2
|
||||
'https://oauth2.googleapis.com/token?' . \http_build_query([
|
||||
'code' => $code,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'redirect_uri' => $this->callback,
|
||||
'scope' => null,
|
||||
'grant_type' => 'authorization_code'
|
||||
@@ -95,7 +95,7 @@ class Google extends OAuth2
|
||||
'https://oauth2.googleapis.com/token?' . \http_build_query([
|
||||
'refresh_token' => $refreshToken,
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'client_secret' => $this->getClientSecret(),
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
@@ -177,4 +177,37 @@ class Google extends OAuth2
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
{
|
||||
$secret = $this->getAppSecret();
|
||||
|
||||
return $secret['clientSecret'] ?? $this->appSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret.
|
||||
* Falls back to treating the raw string as the client secret for backwards compatibility.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
{
|
||||
try {
|
||||
$secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Throwable $th) {
|
||||
return ['clientSecret' => $this->appSecret];
|
||||
}
|
||||
|
||||
if (!\is_array($secret)) {
|
||||
return ['clientSecret' => $this->appSecret];
|
||||
}
|
||||
|
||||
return $secret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use Appwrite\SDK\Language\Android;
|
||||
use Appwrite\SDK\Language\Apple;
|
||||
use Appwrite\SDK\Language\ClaudePlugin;
|
||||
use Appwrite\SDK\Language\CLI;
|
||||
use Appwrite\SDK\Language\CodexPlugin;
|
||||
use Appwrite\SDK\Language\CursorPlugin;
|
||||
use Appwrite\SDK\Language\Dart;
|
||||
use Appwrite\SDK\Language\Deno;
|
||||
@@ -455,6 +456,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
case 'claude-plugin':
|
||||
$config = new ClaudePlugin();
|
||||
break;
|
||||
case 'codex-plugin':
|
||||
$config = new CodexPlugin();
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('Language "' . $language['key'] . '" not supported');
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ class Comment
|
||||
|
||||
protected string $statePrefix = '[appwrite]: #';
|
||||
|
||||
protected ?string $tip = null;
|
||||
|
||||
/**
|
||||
* @var mixed[] $builds
|
||||
*/
|
||||
@@ -81,7 +83,14 @@ class Comment
|
||||
|
||||
public function generateComment(): string
|
||||
{
|
||||
$json = \json_encode($this->builds);
|
||||
if ($this->tip === null) {
|
||||
$this->tip = $this->tips[\array_rand($this->tips)];
|
||||
}
|
||||
|
||||
$json = \json_encode([
|
||||
'builds' => $this->builds,
|
||||
'tip' => $this->tip,
|
||||
]);
|
||||
|
||||
$text = $this->statePrefix . \base64_encode($json) . "\n\n";
|
||||
|
||||
@@ -226,8 +235,7 @@ class Comment
|
||||
$i++;
|
||||
}
|
||||
|
||||
$tip = $this->tips[array_rand($this->tips)];
|
||||
$text .= "\n<br>\n\n> [!TIP]\n> $tip\n\n";
|
||||
$text .= "\n<br>\n\n> [!TIP]\n> {$this->tip}\n\n";
|
||||
|
||||
return $text;
|
||||
}
|
||||
@@ -252,8 +260,15 @@ class Comment
|
||||
|
||||
$json = \base64_decode($state);
|
||||
|
||||
$builds = \json_decode($json, true);
|
||||
$this->builds = \is_array($builds) ? $builds : [];
|
||||
$data = \json_decode($json, true);
|
||||
|
||||
if (\is_array($data) && \array_key_exists('builds', $data)) {
|
||||
$this->builds = \is_array($data['builds']) ? $data['builds'] : [];
|
||||
$this->tip = $data['tip'] ?? null;
|
||||
} else {
|
||||
// Backward compatibility with old state format (builds array only)
|
||||
$this->builds = \is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Vcs;
|
||||
|
||||
use Appwrite\Vcs\Comment;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class CommentTest extends TestCase
|
||||
{
|
||||
public function testTipIsPreservedAcrossMultipleGenerations(): void
|
||||
{
|
||||
$comment = new Comment(['consoleHostname' => 'localhost']);
|
||||
$comment->addBuild(
|
||||
new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']),
|
||||
new Document(['$id' => 'func1', 'name' => 'Test Function']),
|
||||
'function',
|
||||
'ready',
|
||||
'dep1',
|
||||
['type' => 'logs'],
|
||||
''
|
||||
);
|
||||
|
||||
$first = $comment->generateComment();
|
||||
$firstTip = $this->extractTip($first);
|
||||
|
||||
$this->assertNotNull($firstTip);
|
||||
$this->assertNotEmpty($firstTip);
|
||||
|
||||
$second = $comment->generateComment();
|
||||
$secondTip = $this->extractTip($second);
|
||||
|
||||
$this->assertEquals($firstTip, $secondTip);
|
||||
}
|
||||
|
||||
public function testTipIsRestoredFromParsedComment(): void
|
||||
{
|
||||
$comment = new Comment(['consoleHostname' => 'localhost']);
|
||||
$comment->addBuild(
|
||||
new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']),
|
||||
new Document(['$id' => 'func1', 'name' => 'Test Function']),
|
||||
'function',
|
||||
'ready',
|
||||
'dep1',
|
||||
['type' => 'logs'],
|
||||
''
|
||||
);
|
||||
|
||||
$original = $comment->generateComment();
|
||||
$originalTip = $this->extractTip($original);
|
||||
|
||||
$parsed = new Comment(['consoleHostname' => 'localhost']);
|
||||
$parsed->parseComment($original);
|
||||
$parsed->addBuild(
|
||||
new Document(['$id' => 'project1', 'name' => 'Test Project', 'region' => 'default']),
|
||||
new Document(['$id' => 'func2', 'name' => 'Another Function']),
|
||||
'function',
|
||||
'building',
|
||||
'dep2',
|
||||
['type' => 'logs'],
|
||||
''
|
||||
);
|
||||
|
||||
$regenerated = $parsed->generateComment();
|
||||
$regeneratedTip = $this->extractTip($regenerated);
|
||||
|
||||
$this->assertEquals($originalTip, $regeneratedTip);
|
||||
}
|
||||
|
||||
public function testBackwardCompatibilityWithOldStateFormat(): void
|
||||
{
|
||||
$oldBuilds = [
|
||||
'project1_func1' => [
|
||||
'projectName' => 'Test Project',
|
||||
'projectId' => 'project1',
|
||||
'region' => 'default',
|
||||
'resourceName' => 'Test Function',
|
||||
'resourceId' => 'func1',
|
||||
'resourceType' => 'function',
|
||||
'buildStatus' => 'ready',
|
||||
'deploymentId' => 'dep1',
|
||||
'action' => ['type' => 'logs'],
|
||||
'previewUrl' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$oldState = '[appwrite]: #' . \base64_encode(\json_encode($oldBuilds)) . "\n\n";
|
||||
$oldState .= "> [!TIP]\n> Old tip that should be ignored\n\n";
|
||||
|
||||
$comment = new Comment(['consoleHostname' => 'localhost']);
|
||||
$comment->parseComment($oldState);
|
||||
|
||||
$new = $comment->generateComment();
|
||||
$newTip = $this->extractTip($new);
|
||||
|
||||
$this->assertNotNull($newTip);
|
||||
$this->assertNotEquals('Old tip that should be ignored', $newTip);
|
||||
$this->assertContains($newTip, $this->getTips());
|
||||
}
|
||||
|
||||
public function testParseOldStateFormatWithOnlyBuilds(): void
|
||||
{
|
||||
$oldBuilds = [
|
||||
'project1_func1' => [
|
||||
'projectName' => 'Test Project',
|
||||
'projectId' => 'project1',
|
||||
'region' => 'default',
|
||||
'resourceName' => 'Test Function',
|
||||
'resourceId' => 'func1',
|
||||
'resourceType' => 'function',
|
||||
'buildStatus' => 'ready',
|
||||
'deploymentId' => 'dep1',
|
||||
'action' => ['type' => 'logs'],
|
||||
'previewUrl' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$state = '[appwrite]: #' . \base64_encode(\json_encode($oldBuilds)) . "\n\n";
|
||||
|
||||
$comment = new Comment(['consoleHostname' => 'localhost']);
|
||||
$comment->parseComment($state);
|
||||
|
||||
$this->assertEquals(false, $comment->isEmpty());
|
||||
|
||||
$first = $comment->generateComment();
|
||||
$firstTip = $this->extractTip($first);
|
||||
|
||||
$this->assertNotNull($firstTip);
|
||||
$this->assertNotEmpty($firstTip);
|
||||
$this->assertContains($firstTip, $this->getTips());
|
||||
|
||||
$second = $comment->generateComment();
|
||||
$secondTip = $this->extractTip($second);
|
||||
|
||||
$this->assertEquals($firstTip, $secondTip);
|
||||
}
|
||||
|
||||
private function extractTip(string $comment): ?string
|
||||
{
|
||||
if (\preg_match('/> \[!TIP\]\n> (.+)/', $comment, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getTips(): array
|
||||
{
|
||||
$reflection = new \ReflectionClass(Comment::class);
|
||||
$property = $reflection->getProperty('tips');
|
||||
|
||||
return $property->getValue(new Comment(['consoleHostname' => 'localhost']));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user