feat: implement custom triggers for VCS builds

This commit is contained in:
harsh mahajan
2026-04-20 11:57:57 +05:30
parent 9f504cd065
commit 88107ee7b3
5 changed files with 140 additions and 8 deletions
+44
View File
@@ -841,6 +841,28 @@ return [
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('providerBranches'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('providerPaths'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
],
'indexes' => [
[
@@ -1320,6 +1342,28 @@ return [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('providerBranches'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
[
'$id' => ID::custom('providerPaths'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 128,
'signed' => true,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
],
'indexes' => [
[
Generated
+12 -3
View File
@@ -8441,9 +8441,18 @@
"time": "2024-11-07T12:36:22+00:00"
}
],
"aliases": [],
"aliases": [
{
"package": "utopia-php/vcs",
"version": "dev-ser-401",
"alias": "3.0.99",
"alias_normalized": "3.0.99.0"
}
],
"minimum-stability": "dev",
"stability-flags": {},
"stability-flags": {
"utopia-php/vcs": 20
},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
@@ -8464,5 +8473,5 @@
"platform-dev": {
"ext-fileinfo": "*"
},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.6.0"
}
@@ -130,7 +130,10 @@ class Update extends Action
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
$providerCommitAuthorUrl = $commitDetails["commitAuthorUrl"] ?? '';
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, true, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
$prFiles = $github->getPullRequestFiles($owner, $providerRepositoryName, $providerPullRequestId);
$providerAffectedFiles = array_column($prFiles, 'filename');
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $providerAffectedFiles, true, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
$response->noContent();
}
@@ -40,6 +40,7 @@ trait Deployment
string $providerCommitMessage,
string $providerCommitUrl,
string $providerPullRequestId,
array $providerAffectedFiles = [],
bool $external,
Database $dbForPlatform,
Authorization $authorization,
@@ -94,6 +95,11 @@ trait Deployment
$resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resourceInternalId = $resource->getSequence();
if (!$this->isResourceBuildable($resource, $providerBranch, $providerAffectedFiles, $logBase)) {
Span::add("{$logBase}.build.skipped", 'true');
continue;
}
$deploymentId = ID::unique();
$repositoryId = $repository->getId();
$repositoryInternalId = $repository->getSequence();
@@ -560,4 +566,72 @@ trait Deployment
{
return System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME);
}
private function isResourceBuildable(Document $resource, string $providerBranch, array $providerAffectedFiles, string $logBase): bool
{
$allowedBranches = $resource->getAttribute('providerBranches', []);
if (!$this->matchesPatterns($providerBranch, $allowedBranches)) {
Span::add("{$logBase}.build.skipped.reason", 'branch');
return false;
}
$allowedPaths = $resource->getAttribute('providerPaths', []);
if (!empty($allowedPaths) && !empty($providerAffectedFiles)) {
$pathMatched = false;
foreach ($providerAffectedFiles as $file) {
if ($this->matchesPatterns($file, $allowedPaths)) {
$pathMatched = true;
break;
}
}
if (!$pathMatched) {
Span::add("{$logBase}.build.skipped.reason", 'path');
return false;
}
}
return true;
}
private function matchesPatterns(string $subject, array $patterns): bool
{
if (empty($patterns)) {
return true;
}
$include = array_filter($patterns, fn ($p) => !str_starts_with($p, '!'));
$exclude = array_filter($patterns, fn ($p) => str_starts_with($p, '!'));
foreach ($include as $pattern) {
if ($this->matchGlob($subject, $pattern)) {
return true;
}
}
foreach ($exclude as $pattern) {
if ($this->matchGlob($subject, substr($pattern, 1))) {
return false;
}
}
return empty($include);
}
private function matchGlob(string $subject, string $pattern): bool
{
$regex = preg_replace_callback(
'/\*\*|\*|\?|[^*?]+/',
static function (array $m): string {
return match ($m[0]) {
'**' => '.*',
'*' => '[^/]*',
'?' => '[^/]',
default => preg_quote($m[0], '/'),
};
},
$pattern
);
return (bool) preg_match('/^' . $regex . '$/', $subject);
}
}
@@ -162,9 +162,10 @@ class Create extends Action
Query::limit(100),
]));
// Create new deployment only on push (not committed by us) and not when branch is deleted
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchDeleted) {
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
// Create new deployment only on push (not committed by us) and not when branch is created or deleted
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) {
$providerAffectedFiles = $parsedPayload['affectedFiles'] ?? [];
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', $providerAffectedFiles, false, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
}
}
@@ -216,7 +217,8 @@ class Create extends Action
Query::orderDesc('$createdAt')
]));
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
$providerAffectedFiles = $parsedPayload['affectedFiles'] ?? [];
$this->createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $providerAffectedFiles, $external, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $platform);
} elseif ($action == "closed") {
// Allowed external contributions cleanup