Fixes and added updateCommitStatus method

This commit is contained in:
Khushboo Verma
2023-05-09 03:58:33 +05:30
parent 8a749c7b08
commit 1311b8d02b
15 changed files with 145 additions and 90 deletions
+3 -29
View File
@@ -87,32 +87,6 @@ _APP_GRAPHQL_MAX_DEPTH=3
DOCKERHUB_PULL_USERNAME=
DOCKERHUB_PULL_PASSWORD=
DOCKERHUB_PULL_EMAIL=
VCS_GITHUB_NAME=appwrite-vcs
VCS_GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA6U8z6jGzlEOHmrCmyzlb5e4Tl4A7PFs5h2RkTrTWNBjmOsOT
/dISrlcDwGwY+sL8GCLjgLwLVLu+87pWDzZrQ4jIl5dHbt/1cx4gBf/S+SU+LVjT
yMIA8htQ1YHgmMtrYOp6bDlwFDB/bDAHihfmf0G4boLd8n0ogC1jlpHl6ywskDvn
3nBhqwK/T+s2N+tDxJV67ywHL/6B2Qtm9ksZtWNgUHnswkItgAwPt7WqSeh4nHN6
OD+6FzT49zdUJ4Yxi77GrRGFVSyA3iwxKMxuEJhqKLnN7ieHW8vB97OWmw6kdSxi
pTzJ93UuaEbq+MsoK97Vh2Ia8zyRc4vsQLbuxwIDAQABAoIBAG637H4eWgdpSKgL
XwwgS1FBe0CxVOseq51p8gQ7409T5aZLfCa/E6hymNAvPGd8MuBNZkOIeaQbeNH3
QrHzdFHXTsJJw1eFptibmjzem8MqfmhMuCyUjJhYiDLacLhyCKI0hCZp15ttLHsu
C9Xn47XOfXTMXAGgjaDOaW1iDYddRiWWhDpllZDk5hlHc6YLFxvhmcDFrB8Dhlf+
09OWFVEIY+fP0D1meQWye7wM59syhWvXJiwwNbbDQn4g2hq5ywCs/X1CXHdY7RsA
j8nh4C/NIImj4hd2Ur/VGuyRcv8/7qx5jWLITvaEP77yqXu9Od+u1obOnkq24Z2J
HE+awJkCgYEA/bg8wSY7LHrCsxRFlDkNA/LZG2YtlYsGp3r2ush6ffDkhG/f9XQm
EcjZTgonNdQdbMiOBmhLqF72j54QBmYzb8j/qwLcK3v7Dw2ohEUe7cudAUflylfZ
OmNNvgHkvjLJhzvFEQJqTygXFc5XmYnqYqnQkCQLTIyzl1HmsqyhlrMCgYEA62gB
TzF9EzjwRUYpeSgClKzpYF0JGrEtk02I13SYmpbr6knhopRCQ8zmYBFDt0ns/J0H
xLKwK6PfJjHOKmfK9958Ylxqhu7v1nu153WSNKbTBj7AYGAIqZ7MLFQzaxUV6Jk4
v2Wd/bzYc5AtbXLwO6TE7+jS8vgepKNllQnF8Z0CgYEAyEteovhkdHvBIpBZUWcs
cDaR0EB8sfab3TGEf/XuRWgbbrXG7sWvnBCcBDCdva/jPtKg1EwXlibJrJ0Sd4rN
Bm198GoEl8t7aenVY/4RKZa5Lp6suG1bju2AHQTvqHr9f2Xe78uhUzYJ62wG2a33
u8XlYTUjOl5FFKtSXTFNK2MCgYEAmf6XnK7LG2AMDcUwLEm4JnpW0OsLoaJ2Jf56
njdv/PDts35RVIGYN7I6om2o1qZNmZNAGbSq+mFhASNjsunjJPMue0nphguYab7T
6G5JDphzQ4qZXnJ0OEHhXr5WRJ6ujlJthQd8qxh2oGKY1RhzfxDyo96y4K7fnOQm
97BFBSECgYEAtmiG5x/torbspu9NoEqU9lGKnLe5+FSRubxbRnMXeCu9yXmLfEaq
bLcrVj6NiIvBt/nXiMK+x0oRb52sWWf8IRNTY9e/h+Mn4cq24PF78F09Ceb4yrU0
7kqwzha2SNqi7Nh1I2UdgaTwXI6em6MEuM5NI9aOBkyllBuqS7aE3+E=
-----END RSA PRIVATE KEY-----"
VCS_GITHUB_APP_ID=321731
VCS_GITHUB_APP_NAME=
VCS_GITHUB_PRIVATE_KEY=
VCS_GITHUB_APP_ID=
+11
View File
@@ -631,6 +631,17 @@ $collections = [
'array' => false,
'filters' => []
],
[
'$id' => ID::custom('repositoryOwner'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => []
],
[
'$id' => ID::custom('resourceId'),
'type' => Database::VAR_STRING,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -447,13 +447,14 @@ App::put('/v1/functions/:functionId')
->param('enabled', true, new Boolean(), 'Is function enabled?', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for vcs deployment.', true)
->param('repositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
->param('repositoryOwner', '', new Text(128, 0), 'Repository Owner of the repo linked to the function', true)
->inject('response')
->inject('dbForProject')
->inject('dbForConsole')
->inject('project')
->inject('user')
->inject('events')
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, string $vcsInstallationId, string $repositoryId, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $eventsInstance) {
->action(function (string $functionId, string $name, array $execute, array $events, string $schedule, int $timeout, bool $enabled, string $vcsInstallationId, string $repositoryId, string $repositoryOwner, Response $response, Database $dbForProject, Database $dbForConsole, Document $project, Document $user, Event $eventsInstance) {
$function = $dbForProject->getDocument('functions', $functionId);
@@ -513,6 +514,7 @@ App::put('/v1/functions/:functionId')
'projectId' => $project->getId(),
'projectInternalId' => $project->getInternalId(),
'repositoryId' => $repositoryId,
'repositoryOwner' => $repositoryOwner,
'resourceId' => $functionId,
'resourceType' => "function"
]);
+14 -8
View File
@@ -37,7 +37,7 @@ App::get('/v1/vcs/github/installations')
->action(function (Response $response, Document $project) {
$projectId = $project->getId();
$appName = App::getEnv('VCS_GITHUB_NAME');
$appName = App::getEnv('VCS_GITHUB_APP_NAME');
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
@@ -129,9 +129,8 @@ App::get('v1/vcs/github/installations/:installationId/repositories')
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$githubAppName = App::getEnv('VCS_GITHUB_NAME');
$github = new GitHub();
$github->initialiseVariables($installationId, $privateKey, $githubAppId, $githubAppName);
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$repos = $github->listRepositoriesForGitHubApp();
$response->dynamic(new Document([
@@ -157,14 +156,14 @@ App::post('/v1/vcs/github/incomingwebhook')
$github = new GitHub();
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$githubAppName = App::getEnv('VCS_GITHUB_NAME');
$parsedPayload = $github->parseWebhookEventPayload($event, $payload);
$parsedPayload = json_decode($parsedPayload, true);
if ($event == $github::EVENT_PUSH) {
$branchName = $parsedPayload["branch"];
$repositoryId = $parsedPayload["repositoryId"];
$installationId = $parsedPayload["installationId"];
$SHA = $parsedPayload["SHA"];
$owner = $parsedPayload["owner"];
//find functionId from functions table
$resources = $dbForConsole->find('vcs_repos', [
@@ -219,12 +218,17 @@ App::post('/v1/vcs/github/incomingwebhook')
'activate' => $activate,
]));
$targetUrl = $request->getProtocol() . '://' . $request->getHostname() . ":3000/console/project-$projectId/functions/function-$functionId";
$buildEvent = new Build();
$buildEvent
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setProject($project)
->setSHA($SHA)
->setOwner($owner)
->setTargetUrl($targetUrl)
->trigger();
//TODO: Add event?
@@ -261,7 +265,8 @@ App::post('/v1/vcs/github/incomingwebhook')
$installationId = $parsedPayload["installationId"];
$pullRequestNumber = $parsedPayload["pullRequestNumber"];
$repositoryName = $parsedPayload["repositoryName"];
$github->initialiseVariables($installationId, $privateKey, $githubAppId, $githubAppName);
$owner = $parsedPayload["owner"];
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$vcsRepos = $dbForConsole->find('vcs_repos', [
Query::equal('repositoryId', [$repositoryId]),
@@ -285,7 +290,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
$buildStatus = $build->getAttribute('status');
$comment = "| Build Status |\r\n | --------------- |\r\n | $buildStatus |";
$commentId = $github->addComment($repositoryName, $pullRequestNumber, $comment);
$commentId = $github->addComment($owner, $repositoryName, $pullRequestNumber, $comment);
} else {
$startNewDeployment = true;
}
@@ -293,7 +298,7 @@ App::post('/v1/vcs/github/incomingwebhook')
$startNewDeployment = true;
}
if ($startNewDeployment) {
$commentId = strval($github->addComment($repositoryName, $pullRequestNumber, "Build is not deployed yet 🚀"));
$commentId = strval($github->addComment($owner, $repositoryName, $pullRequestNumber, "Build is not deployed yet 🚀"));
foreach ($vcsRepos as $resource) {
$resourceType = $resource->getAttribute('resourceType');
@@ -347,6 +352,7 @@ App::post('/v1/vcs/github/incomingwebhook')
->setResource($function)
->setDeployment($deployment)
->setProject($project)
->setOwner($owner)
->trigger();
//TODO: Add event?
+24 -9
View File
@@ -46,12 +46,15 @@ class BuildsV1 extends Worker
$project = new Document($this->args['project'] ?? []);
$resource = new Document($this->args['resource'] ?? []);
$deployment = new Document($this->args['deployment'] ?? []);
$SHA = $this->args['SHA'] ?? '';
$owner = $this->args['owner'] ?? '';
$targetUrl = $this->args['targetUrl'] ?? '';
switch ($type) {
case BUILD_TYPE_DEPLOYMENT:
case BUILD_TYPE_RETRY:
Console::info('Creating build for deployment: ' . $deployment->getId());
$this->buildDeployment($project, $resource, $deployment);
$this->buildDeployment($project, $resource, $deployment, $SHA, $owner, $targetUrl);
break;
default:
@@ -60,7 +63,7 @@ class BuildsV1 extends Worker
}
}
protected function buildDeployment(Document $project, Document $function, Document $deployment)
protected function buildDeployment(Document $project, Document $function, Document $deployment, string $SHA = '', string $owner = '', string $targetUrl = '')
{
$dbForProject = $this->getProjectDB($project->getId());
$dbForConsole = $this->getConsoleDB();
@@ -96,19 +99,19 @@ class BuildsV1 extends Worker
$vcsRepos = Authorization::skip(fn () => $dbForConsole
->getDocument('vcs_repos', $vcsRepoId));
$repositoryId = $vcsRepos->getAttribute('repositoryId');
$owner = $vcsRepos->getAttribute('repositoryOwner');
$vcsInstallations = Authorization::skip(fn () => $dbForConsole
->getDocument('vcs_installations', $vcsInstallationId));
$installationId = $vcsInstallations->getAttribute('installationId');
$privateKey = App::getEnv('VCS_GITHUB_PRIVATE_KEY');
$githubAppId = App::getEnv('VCS_GITHUB_APP_ID');
$githubAppName = App::getEnv('VCS_GITHUB_NAME');
$github = new GitHub();
$github->initialiseVariables($installationId, $privateKey, $githubAppId, $githubAppName);
$github->initialiseVariables($installationId, $privateKey, $githubAppId);
$repositoryName = $github->getRepositoryName($repositoryId);
$branchName = $deployment->getAttribute('branch');
$gitCloneCommand = $github->generateGitCloneCommand($repositoryId, $branchName);
$gitCloneCommand = $github->generateGitCloneCommand($owner, $repositoryId, $branchName);
$stdout = '';
$stderr = '';
Console::execute('mkdir /tmp/builds/' . $buildId, '', $stdout, $stderr);
@@ -146,11 +149,14 @@ class BuildsV1 extends Worker
'endTime' => null,
'duration' => 0
]));
if ($SHA !== "" && $owner !== ""){
$github->updateCommitStatus($repositoryName, $SHA, $owner, "pending", "Deployment is being processed..", $targetUrl, "Appwrite Deployment");
}
$commentId = $deployment->getAttribute('vcsCommentId');
if ($commentId) {
$comment = "| Build Status |\r\n | --------------- |\r\n | Processing |";
$github->updateComment($repositoryName, $commentId, $comment);
$github->updateComment($owner, $repositoryName, $commentId, $comment);
$addComment = true;
}
} else {
@@ -182,7 +188,7 @@ class BuildsV1 extends Worker
$commentId = $deployment->getAttribute('vcsCommentId');
if ($commentId) {
$comment = "| Build Status |\r\n | --------------- |\r\n | Building |";
$github->updateComment($repositoryName, $commentId, $comment);
$github->updateComment($owner, $repositoryName, $commentId, $comment);
}
}
$build = $dbForProject->updateDocument('builds', $buildId, $build);
@@ -266,11 +272,20 @@ class BuildsV1 extends Worker
$build->setAttribute('stdout', $response['response']);
if ($isVcsEnabled) {
$status = $response["status"];
if ($status === "ready" && $SHA !== "" && $owner !== "") {
$github->updateCommitStatus($repositoryName, $SHA, $owner, "success", "Deployment is successful!", $targetUrl, "Appwrite Deployment");
}
else if ($status === "failed" && $SHA !== "" && $owner !== "") {
$github->updateCommitStatus($repositoryName, $SHA, $owner, "failure", "Deployment failed.", $targetUrl, "Appwrite Deployment");
}
$commentId = $deployment->getAttribute('vcsCommentId');
if ($commentId) {
$status = $response["status"];
$comment = "| Build Status |\r\n | --------------- |\r\n | $status |";
$github->updateComment($repositoryName, $commentId, $comment);
$github->updateComment($owner, $repositoryName, $commentId, $comment);
}
}
Generated
+28 -32
View File
@@ -3156,11 +3156,12 @@
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "18a6aa235362e0cc08e47670452dea89bcbb3548"
"reference": "018042e40350689d59bf5e31f973a76c1646d28d"
},
"require": {
"adhocore/jwt": "^1.1",
"php": ">=8.0",
"utopia-php/framework": "^0.28.1",
"vlucas/phpdotenv": "^5.5"
},
"require-dev": {
@@ -3199,7 +3200,7 @@
"utopia",
"vcs"
],
"time": "2023-04-23T19:34:53+00:00"
"time": "2023-05-08T19:32:37+00:00"
},
{
"name": "utopia-php/websocket",
@@ -3471,16 +3472,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.32.2",
"version": "0.32.3",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "cdec289bcf38c99d0074414d2438e9967d0c9699"
"reference": "4057e14a61335070034b1cbdce9e39bef94d997d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/cdec289bcf38c99d0074414d2438e9967d0c9699",
"reference": "cdec289bcf38c99d0074414d2438e9967d0c9699",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/4057e14a61335070034b1cbdce9e39bef94d997d",
"reference": "4057e14a61335070034b1cbdce9e39bef94d997d",
"shasum": ""
},
"require": {
@@ -3516,9 +3517,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/0.32.2"
"source": "https://github.com/appwrite/sdk-generator/tree/0.32.3"
},
"time": "2023-04-12T21:06:57+00:00"
"time": "2023-04-27T19:22:05+00:00"
},
{
"name": "doctrine/deprecations",
@@ -4221,16 +4222,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.20.2",
"version": "1.20.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81"
"reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/90490bd8fd8530a272043c4950c180b6d0cf5f81",
"reference": "90490bd8fd8530a272043c4950c180b6d0cf5f81",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
"reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd",
"shasum": ""
},
"require": {
@@ -4260,9 +4261,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.2"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4"
},
"time": "2023-04-22T12:59:35+00:00"
"time": "2023-05-02T09:19:37+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -4985,16 +4986,16 @@
},
{
"name": "sebastian/diff",
"version": "4.0.4",
"version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"shasum": ""
},
"require": {
@@ -5039,7 +5040,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
},
"funding": [
{
@@ -5047,7 +5048,7 @@
"type": "github"
}
],
"time": "2020-10-26T13:10:38+00:00"
"time": "2023-05-07T05:35:17+00:00"
},
{
"name": "sebastian/environment",
@@ -5849,16 +5850,16 @@
},
{
"name": "twig/twig",
"version": "v3.5.1",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15"
"reference": "106c170d08e8415d78be2d16c3d057d0d108262b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15",
"reference": "a6e0510cc793912b451fd40ab983a1d28f611c15",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/106c170d08e8415d78be2d16c3d057d0d108262b",
"reference": "106c170d08e8415d78be2d16c3d057d0d108262b",
"shasum": ""
},
"require": {
@@ -5867,15 +5868,10 @@
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"psr/container": "^1.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.5-dev"
}
},
"autoload": {
"psr-4": {
"Twig\\": "src/"
@@ -5909,7 +5905,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.5.1"
"source": "https://github.com/twigphp/Twig/tree/v3.6.0"
},
"funding": [
{
@@ -5921,7 +5917,7 @@
"type": "tidelift"
}
],
"time": "2023-02-08T07:49:20+00:00"
"time": "2023-05-03T19:06:57+00:00"
}
],
"aliases": [
+2 -2
View File
@@ -179,7 +179,7 @@ services:
- _APP_GRAPHQL_MAX_DEPTH
- VCS_GITHUB_PRIVATE_KEY
- VCS_GITHUB_APP_ID
- VCS_GITHUB_NAME
- VCS_GITHUB_APP_NAME
appwrite-realtime:
entrypoint: realtime
@@ -400,7 +400,7 @@ services:
- _APP_GITHUB_APP_ID
- VCS_GITHUB_PRIVATE_KEY
- VCS_GITHUB_APP_ID
- VCS_GITHUB_NAME
- VCS_GITHUB_APP_NAME
appwrite-worker-certificates:
entrypoint: worker-certificates
+46 -1
View File
@@ -10,6 +10,9 @@ class Build extends Event
protected string $type = '';
protected ?Document $resource = null;
protected ?Document $deployment = null;
protected string $SHA = '';
protected string $owner = '';
protected string $targetUrl = '';
public function __construct()
{
@@ -85,6 +88,45 @@ class Build extends Event
return $this->type;
}
/**
* Sets commit SHA for the build event.
*
* @param string $SHA is the commit hash of the incoming commit
* @return self
*/
public function setSHA(string $SHA): self
{
$this->SHA = $SHA;
return $this;
}
/**
* Sets repository owner name for the build event.
*
* @param string $owner is the name of the repository owner
* @return self
*/
public function setOwner(string $owner): self
{
$this->owner = $owner;
return $this;
}
/**
* Sets redirect target url for the deployment
*
* @param string $targetUrl is the url that is to be set
* @return self
*/
public function setTargetUrl(string $targetUrl): self
{
$this->targetUrl = $targetUrl;
return $this;
}
/**
* Executes the function event and sends it to the functions worker.
*
@@ -97,7 +139,10 @@ class Build extends Event
'project' => $this->project,
'resource' => $this->resource,
'deployment' => $this->deployment,
'type' => $this->type
'type' => $this->type,
'SHA' => $this->SHA,
'owner' => $this->owner,
'targetUrl' => $this->targetUrl
]);
}
}
@@ -17,10 +17,16 @@ class Repository extends Model
'example' => '5e5ea5c16897e',
])
->addRule('name', [
'type' => self::TYPE_DATETIME,
'type' => self::TYPE_STRING,
'description' => 'Repository Name.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
'example' => 'appwrite',
])
->addRule('owner', [
'type' => self::TYPE_JSON,
'description' => 'Repository Owner.',
'default' => '',
'example' => '{"login": "Example Owner"}',
])
;
}