From 2836f89b3440505ed14214707e71e0118f66d6f9 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 4 May 2026 13:09:38 +0530 Subject: [PATCH 01/15] feat: skip deployment on commit message pattern match Add providerCommitSkipPatterns array field to functions and sites. Any commit message containing one of the patterns (case-insensitive substring) skips the VCS-triggered deployment. Co-Authored-By: Claude Sonnet 4.6 --- app/config/collections/projects.php | 22 +++ .../Modules/VCS/Http/GitHub/Deployment.php | 17 +++ .../Vcs/Validator/CommitSkipPatterns.php | 50 +++++++ .../Vcs/Validator/CommitSkipPatternsTest.php | 141 ++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 src/Appwrite/Vcs/Validator/CommitSkipPatterns.php create mode 100644 tests/unit/Vcs/Validator/CommitSkipPatternsTest.php diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 9568c59369..0f1bfe12f5 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -841,6 +841,17 @@ return [ 'array' => true, 'filters' => [], ], + [ + '$id' => ID::custom('providerCommitSkipPatterns'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => true, + 'filters' => [], + ], ], 'indexes' => [ [ @@ -1320,6 +1331,17 @@ return [ 'array' => false, 'filters' => [], ], + [ + '$id' => ID::custom('providerCommitSkipPatterns'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 128, + 'signed' => true, + 'required' => false, + 'default' => [], + 'array' => true, + 'filters' => [], + ], ], 'indexes' => [ [ diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index 8bc090bb03..a6124412bb 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -8,6 +8,7 @@ use Appwrite\Event\Publisher\Build as BuildPublisher; use Appwrite\Extend\Exception; use Appwrite\Filter\BranchDomain as BranchDomainFilter; use Appwrite\Vcs\Comment; +use Appwrite\Vcs\Validator\CommitSkipPatterns; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -95,6 +96,11 @@ trait Deployment $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); + if (!$this->isResourceBuildable($resource, $logBase, $providerCommitMessage)) { + Span::add("{$logBase}.build.skipped", 'true'); + continue; + } + $deploymentId = ID::unique(); $repositoryId = $repository->getId(); $repositoryInternalId = $repository->getSequence(); @@ -561,4 +567,15 @@ trait Deployment { return System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME); } + + private function isResourceBuildable(Document $resource, string $logBase, string $providerCommitMessage = ''): bool + { + $commitSkip = new CommitSkipPatterns($resource->getAttribute('providerCommitSkipPatterns', [])); + if (!$commitSkip->isValid($providerCommitMessage)) { + Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); + return false; + } + + return true; + } } diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php new file mode 100644 index 0000000000..76c04e5417 --- /dev/null +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -0,0 +1,50 @@ +patterns as $pattern) { + if (!is_string($pattern) || $pattern === '') { + continue; + } + if (stripos($value, $pattern) !== false) { + return false; + } + } + + return true; + } + + public function getDescription(): string + { + return 'Commit message must not contain any of the configured skip patterns.'; + } + + public function isArray(): bool + { + return false; + } + + public function getType(): string + { + return self::TYPE_STRING; + } +} diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php new file mode 100644 index 0000000000..3725c643bc --- /dev/null +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -0,0 +1,141 @@ +assertTrue($validator->isValid('fix: update readme')); + $this->assertTrue($validator->isValid('[skip deploy] docs only')); + $this->assertTrue($validator->isValid('')); + } + + // ------------------------------------------------------------------------- + // Single pattern — exact substring + // ------------------------------------------------------------------------- + + public function testSinglePatternMatchSkips(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertFalse($validator->isValid('[skip deploy] docs only')); + $this->assertFalse($validator->isValid('chore: update deps [skip deploy]')); + $this->assertFalse($validator->isValid('prefix [skip deploy] suffix')); + } + + public function testSinglePatternNoMatchProceeds(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertTrue($validator->isValid('fix: real bug fix')); + $this->assertTrue($validator->isValid('feat: add new feature')); + $this->assertTrue($validator->isValid('skip deploy without brackets')); + } + + // ------------------------------------------------------------------------- + // Case insensitivity + // ------------------------------------------------------------------------- + + public function testCaseInsensitiveMatch(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertFalse($validator->isValid('[SKIP DEPLOY] uppercase')); + $this->assertFalse($validator->isValid('[Skip Deploy] mixed case')); + $this->assertFalse($validator->isValid('[skip DEPLOY] partial upper')); + } + + public function testPatternItselfCaseInsensitive(): void + { + $validator = new CommitSkipPatterns(['[SKIP DEPLOY]']); + $this->assertFalse($validator->isValid('[skip deploy] lowercase message')); + $this->assertFalse($validator->isValid('[Skip Deploy] mixed message')); + } + + // ------------------------------------------------------------------------- + // Array of patterns — any match skips (OR semantics) + // ------------------------------------------------------------------------- + + public function testMultiplePatternsFirstMatches(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); + $this->assertFalse($validator->isValid('[skip deploy] docs only')); + } + + public function testMultiplePatternsSecondMatches(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); + $this->assertFalse($validator->isValid('chore: update readme [skip ci]')); + } + + public function testMultiplePatternsThirdMatches(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); + $this->assertFalse($validator->isValid('[no deploy] just docs')); + } + + public function testMultiplePatternsNoneMatchProceeds(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); + $this->assertTrue($validator->isValid('feat: completely new feature')); + $this->assertTrue($validator->isValid('fix: important bug fix')); + } + + // ------------------------------------------------------------------------- + // Common real-world skip conventions + // ------------------------------------------------------------------------- + + public function testCommonSkipCiPattern(): void + { + $validator = new CommitSkipPatterns(['[skip ci]']); + $this->assertFalse($validator->isValid('[skip ci] update changelog')); + $this->assertFalse($validator->isValid('[SKIP CI]')); + $this->assertTrue($validator->isValid('feat: something real')); + } + + public function testNoDeployPattern(): void + { + $validator = new CommitSkipPatterns(['[no deploy]']); + $this->assertFalse($validator->isValid('[no deploy] tweak docs')); + $this->assertTrue($validator->isValid('deploy this please')); + } + + // ------------------------------------------------------------------------- + // Edge cases + // ------------------------------------------------------------------------- + + public function testEmptyCommitMessageNeverSkipsWithPatterns(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertTrue($validator->isValid('')); + } + + public function testBlankPatternsInArrayAreIgnored(): void + { + $validator = new CommitSkipPatterns(['', ' ', '[skip deploy]']); + // empty/whitespace-only patterns must not cause a false positive on empty messages + $this->assertTrue($validator->isValid('normal commit message')); + // but the real pattern still works + $this->assertFalse($validator->isValid('[skip deploy] docs')); + } + + public function testPatternAsSubstringOfLongerWord(): void + { + // "skip" is a substring of "skippy" — should NOT accidentally skip + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertTrue($validator->isValid('skippy the kangaroo')); + } + + public function testMultilineCommitMessage(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $msg = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; + $this->assertFalse($validator->isValid($msg)); + } +} From 4a4f51622d24b0f96a538225200b45fb640ceb29 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Tue, 5 May 2026 18:23:14 +0530 Subject: [PATCH 02/15] fix: tighten commit skip directive matching --- .../Vcs/Validator/CommitSkipPatterns.php | 74 +++++++++++++++++-- .../Vcs/Validator/CommitSkipPatternsTest.php | 23 ++++-- 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index 76c04e5417..7243808bf8 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -12,7 +12,7 @@ class CommitSkipPatterns extends Validator /** * Returns false (skip deployment) when the commit message contains any of the - * configured patterns (case-insensitive substring match). + * configured skip directives. * Returns true (proceed) when no patterns are configured or none match. */ public function isValid($value): bool @@ -21,11 +21,13 @@ class CommitSkipPatterns extends Validator return false; } - foreach ($this->patterns as $pattern) { - if (!is_string($pattern) || $pattern === '') { - continue; - } - if (stripos($value, $pattern) !== false) { + $patterns = $this->normalizePatterns($this->patterns); + if (empty($patterns)) { + return true; + } + + foreach ($this->extractDirectives($value) as $directive) { + if (isset($patterns[$directive])) { return false; } } @@ -47,4 +49,64 @@ class CommitSkipPatterns extends Validator { return self::TYPE_STRING; } + + /** + * @param array $patterns + * @return array + */ + private function normalizePatterns(array $patterns): array + { + $normalized = []; + + foreach ($patterns as $pattern) { + if (!\is_string($pattern)) { + continue; + } + + $pattern = $this->normalizeDirective($pattern); + if ($pattern === '') { + continue; + } + + $normalized[$pattern] = true; + } + + return $normalized; + } + + /** + * @return array + */ + private function extractDirectives(string $message): array + { + $directives = []; + + if (\preg_match_all('/\[[^\]\r\n]+\]/u', $message, $matches) > 0) { + foreach ($matches[0] as $match) { + $directives[] = $this->normalizeDirective($match); + } + } + + foreach (\preg_split("/\r\n|\n|\r/", $message) ?: [] as $line) { + $line = \trim($line); + if ($line === '' || !\str_contains($line, ':')) { + continue; + } + + $directives[] = $this->normalizeDirective($line); + } + + return \array_values(\array_filter(\array_unique($directives))); + } + + private function normalizeDirective(string $value): string + { + $value = \trim($value); + if ($value === '') { + return ''; + } + + $value = (string) \preg_replace('/\s+/u', ' ', $value); + return \mb_strtolower($value); + } } diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index 3725c643bc..efc88bbf6a 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -20,7 +20,7 @@ class CommitSkipPatternsTest extends TestCase } // ------------------------------------------------------------------------- - // Single pattern — exact substring + // Single pattern — directive match // ------------------------------------------------------------------------- public function testSinglePatternMatchSkips(): void @@ -37,6 +37,7 @@ class CommitSkipPatternsTest extends TestCase $this->assertTrue($validator->isValid('fix: real bug fix')); $this->assertTrue($validator->isValid('feat: add new feature')); $this->assertTrue($validator->isValid('skip deploy without brackets')); + $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); } // ------------------------------------------------------------------------- @@ -119,17 +120,15 @@ class CommitSkipPatternsTest extends TestCase public function testBlankPatternsInArrayAreIgnored(): void { $validator = new CommitSkipPatterns(['', ' ', '[skip deploy]']); - // empty/whitespace-only patterns must not cause a false positive on empty messages $this->assertTrue($validator->isValid('normal commit message')); - // but the real pattern still works $this->assertFalse($validator->isValid('[skip deploy] docs')); } - public function testPatternAsSubstringOfLongerWord(): void + public function testPatternMustBeStandaloneDirective(): void { - // "skip" is a substring of "skippy" — should NOT accidentally skip $validator = new CommitSkipPatterns(['[skip deploy]']); $this->assertTrue($validator->isValid('skippy the kangaroo')); + $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); } public function testMultilineCommitMessage(): void @@ -138,4 +137,18 @@ class CommitSkipPatternsTest extends TestCase $msg = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; $this->assertFalse($validator->isValid($msg)); } + + public function testWhitespaceInsideDirectiveIsNormalized(): void + { + $validator = new CommitSkipPatterns([' [skip deploy] ']); + $this->assertFalse($validator->isValid('[skip deploy] docs only')); + $this->assertFalse($validator->isValid('[SKIP DEPLOY] docs only')); + } + + public function testTrailerDirectiveCanSkip(): void + { + $validator = new CommitSkipPatterns(['skip-checks: true']); + $msg = "feat: add new stuff\n\nMore detail here.\n\nskip-checks:true"; + $this->assertFalse($validator->isValid($msg)); + } } From 444d4b4e66928ec2ca0d52528dbf3eb4add7730c Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 7 May 2026 18:25:39 +0530 Subject: [PATCH 03/15] refactor: standalone regex matching for commit skip patterns Replace directive-extraction approach with word-boundary regex matching so plain-word patterns like "skip appwrite" and "appwrite skip" work alongside bracket directives. Use \s+ between word tokens (required space) and \s* only after ":" tokens (git trailer flexibility). Add tests for "skip appwrite" and "appwrite skip" with case insensitivity. Co-Authored-By: Claude Sonnet 4.6 --- .../Vcs/Validator/CommitSkipPatterns.php | 107 +++++++----------- .../Vcs/Validator/CommitSkipPatternsTest.php | 61 ++++++++++ 2 files changed, 101 insertions(+), 67 deletions(-) diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index 7243808bf8..9dbe45ba83 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -12,8 +12,16 @@ class CommitSkipPatterns extends Validator /** * Returns false (skip deployment) when the commit message contains any of the - * configured skip directives. + * configured patterns as a standalone directive (case-insensitive). * Returns true (proceed) when no patterns are configured or none match. + * + * Matching rules: + * - Case-insensitive + * - The directive must be surrounded by whitespace or string boundaries, so + * "prefix[skip deploy]suffix" does NOT accidentally skip + * - Internal whitespace in the pattern is normalised: tokens are split on \s+ + * and rejoined with \s* in the regex, so "[skip deploy]" matches + * "[skip deploy]" and "skip-checks: true" matches "skip-checks:true" */ public function isValid($value): bool { @@ -21,13 +29,38 @@ class CommitSkipPatterns extends Validator return false; } - $patterns = $this->normalizePatterns($this->patterns); - if (empty($patterns)) { - return true; - } + foreach ($this->patterns as $pattern) { + if (!is_string($pattern)) { + continue; + } - foreach ($this->extractDirectives($value) as $directive) { - if (isset($patterns[$directive])) { + $pattern = trim($pattern); + if ($pattern === '') { + continue; + } + + // Split on whitespace; each token is regex-quoted. Tokens are rejoined + // with \s+ (required space) so that "skipappwrite" does NOT match the + // pattern "skip appwrite". The only exception: when the preceding token + // ends with ":" (git trailer style), \s* is used so that + // "skip-checks:true" still matches the pattern "skip-checks: true". + $tokens = preg_split('/\s+/', $pattern); + $regexParts = []; + $count = count($tokens); + for ($i = 0; $i < $count; $i++) { + $regexParts[] = preg_quote($tokens[$i], '~'); + if ($i < $count - 1) { + $regexParts[] = str_ends_with($tokens[$i], ':') ? '\s*' : '\s+'; + } + } + $regexBody = implode('', $regexParts); + + // (? $patterns - * @return array - */ - private function normalizePatterns(array $patterns): array - { - $normalized = []; - - foreach ($patterns as $pattern) { - if (!\is_string($pattern)) { - continue; - } - - $pattern = $this->normalizeDirective($pattern); - if ($pattern === '') { - continue; - } - - $normalized[$pattern] = true; - } - - return $normalized; - } - - /** - * @return array - */ - private function extractDirectives(string $message): array - { - $directives = []; - - if (\preg_match_all('/\[[^\]\r\n]+\]/u', $message, $matches) > 0) { - foreach ($matches[0] as $match) { - $directives[] = $this->normalizeDirective($match); - } - } - - foreach (\preg_split("/\r\n|\n|\r/", $message) ?: [] as $line) { - $line = \trim($line); - if ($line === '' || !\str_contains($line, ':')) { - continue; - } - - $directives[] = $this->normalizeDirective($line); - } - - return \array_values(\array_filter(\array_unique($directives))); - } - - private function normalizeDirective(string $value): string - { - $value = \trim($value); - if ($value === '') { - return ''; - } - - $value = (string) \preg_replace('/\s+/u', ' ', $value); - return \mb_strtolower($value); - } } diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index efc88bbf6a..b546199d22 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -151,4 +151,65 @@ class CommitSkipPatternsTest extends TestCase $msg = "feat: add new stuff\n\nMore detail here.\n\nskip-checks:true"; $this->assertFalse($validator->isValid($msg)); } + + // ------------------------------------------------------------------------- + // Plain-word patterns: "skip appwrite" and "appwrite skip" + // ------------------------------------------------------------------------- + + public function testSkipAppwritePatternSkips(): void + { + $validator = new CommitSkipPatterns(['skip appwrite']); + $this->assertFalse($validator->isValid('docs: update readme skip appwrite')); + $this->assertFalse($validator->isValid('skip appwrite')); + } + + public function testSkipAppwritePatternCaseInsensitive(): void + { + $validator = new CommitSkipPatterns(['skip appwrite']); + $this->assertFalse($validator->isValid('SKIP APPWRITE')); + $this->assertFalse($validator->isValid('Skip Appwrite')); + $this->assertFalse($validator->isValid('SKIP appwrite')); + } + + public function testSkipAppwritePatternNoMatchProceeds(): void + { + $validator = new CommitSkipPatterns(['skip appwrite']); + $this->assertTrue($validator->isValid('feat: real feature')); + $this->assertTrue($validator->isValid('skipappwrite')); // no space — not standalone + $this->assertTrue($validator->isValid('appwrite is great')); + } + + public function testAppwriteSkipPatternSkips(): void + { + $validator = new CommitSkipPatterns(['appwrite skip']); + $this->assertFalse($validator->isValid('appwrite skip ci')); + $this->assertFalse($validator->isValid('docs appwrite skip')); + $this->assertFalse($validator->isValid('appwrite skip')); + } + + public function testAppwriteSkipPatternCaseInsensitive(): void + { + $validator = new CommitSkipPatterns(['appwrite skip']); + $this->assertFalse($validator->isValid('APPWRITE SKIP')); + $this->assertFalse($validator->isValid('Appwrite Skip')); + $this->assertFalse($validator->isValid('appwrite SKIP')); + } + + public function testAppwriteSkipPatternNoMatchProceeds(): void + { + $validator = new CommitSkipPatterns(['appwrite skip']); + $this->assertTrue($validator->isValid('feat: deploy appwrite changes')); + $this->assertTrue($validator->isValid('appwriteskip')); // no space — not standalone + $this->assertTrue($validator->isValid('skip the appwrite stuff')); + } + + public function testBothAppwritePatternsInArray(): void + { + $validator = new CommitSkipPatterns(['skip appwrite', 'appwrite skip']); + $this->assertFalse($validator->isValid('skip appwrite')); + $this->assertFalse($validator->isValid('appwrite skip')); + $this->assertFalse($validator->isValid('SKIP APPWRITE')); + $this->assertFalse($validator->isValid('APPWRITE SKIP')); + $this->assertTrue($validator->isValid('feat: deploy appwrite changes')); + } } From 4684688fc7e5b5568d12dd253e796b25168bb690 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 17:16:03 +0530 Subject: [PATCH 04/15] feat: expose commit skip patterns --- .../Platform/Modules/Functions/Http/Functions/Create.php | 3 +++ .../Platform/Modules/Functions/Http/Functions/Update.php | 3 +++ src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php | 4 ++++ src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php | 5 +++++ src/Appwrite/Utopia/Response/Model/Func.php | 7 +++++++ src/Appwrite/Utopia/Response/Model/Site.php | 7 +++++++ 6 files changed, 29 insertions(+) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 00a91141fb..b9bd67b1e0 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -94,6 +94,7 @@ class Create extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) + ->param('providerCommitSkipPatterns', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -146,6 +147,7 @@ class Create extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, + array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, string $templateRepository, @@ -247,6 +249,7 @@ class Create extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, + 'providerCommitSkipPatterns' => $providerCommitSkipPatterns, 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, ])); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index b3fcb2c021..39b56bb39e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -87,6 +87,7 @@ class Update extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) + ->param('providerCommitSkipPatterns', null, new Nullable(new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -132,6 +133,7 @@ class Update extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, + ?array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -276,6 +278,7 @@ class Update extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, + 'providerCommitSkipPatterns' => $providerCommitSkipPatterns ?? $function->getAttribute('providerCommitSkipPatterns', []), 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'search' => implode(' ', [$functionId, $name, $runtime]), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php index d01d0d8ca7..95c34ce686 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php @@ -19,6 +19,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; +use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\Text; @@ -78,6 +79,7 @@ class Create extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true) + ->param('providerCommitSkipPatterns', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -118,6 +120,7 @@ class Create extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, + array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -173,6 +176,7 @@ class Create extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, + 'providerCommitSkipPatterns' => $providerCommitSkipPatterns, 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'buildRuntime' => $buildRuntime, diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 2aee03265e..9e5184abd0 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -22,7 +22,9 @@ use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; +use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -81,6 +83,7 @@ class Update extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true) + ->param('providerCommitSkipPatterns', null, new Nullable(new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -126,6 +129,7 @@ class Update extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, + ?array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -271,6 +275,7 @@ class Update extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, + 'providerCommitSkipPatterns' => $providerCommitSkipPatterns ?? $site->getAttribute('providerCommitSkipPatterns', []), 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'search' => implode(' ', [$siteId, $name, $framework]), diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php index 3aea364fe5..a85014b9d8 100644 --- a/src/Appwrite/Utopia/Response/Model/Func.php +++ b/src/Appwrite/Utopia/Response/Model/Func.php @@ -182,6 +182,13 @@ class Func extends Model 'default' => false, 'example' => false, ]) + ->addRule('providerCommitSkipPatterns', [ + 'type' => self::TYPE_STRING, + 'description' => 'Commit message patterns that skip automatic deployments', + 'default' => [], + 'example' => ['[skip deploy]'], + 'array' => true, + ]) ->addRule('buildSpecification', [ 'type' => self::TYPE_STRING, 'description' => 'Machine specification for deployment builds.', diff --git a/src/Appwrite/Utopia/Response/Model/Site.php b/src/Appwrite/Utopia/Response/Model/Site.php index 941b6104df..8bb33f6000 100644 --- a/src/Appwrite/Utopia/Response/Model/Site.php +++ b/src/Appwrite/Utopia/Response/Model/Site.php @@ -173,6 +173,13 @@ class Site extends Model 'default' => false, 'example' => false, ]) + ->addRule('providerCommitSkipPatterns', [ + 'type' => self::TYPE_STRING, + 'description' => 'Commit message patterns that skip automatic deployments', + 'default' => [], + 'example' => ['[skip deploy]'], + 'array' => true, + ]) ->addRule('buildSpecification', [ 'type' => self::TYPE_STRING, 'description' => 'Machine specification for deployment builds.', From 73eb14d4bb724b84c6c545741243ce612b64c1ee Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 17:19:55 +0530 Subject: [PATCH 05/15] fix: fail open for non-string commit messages --- src/Appwrite/Vcs/Validator/CommitSkipPatterns.php | 2 +- tests/unit/Vcs/Validator/CommitSkipPatternsTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index 9dbe45ba83..fc5dec2de3 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -26,7 +26,7 @@ class CommitSkipPatterns extends Validator public function isValid($value): bool { if (!is_string($value)) { - return false; + return true; } foreach ($this->patterns as $pattern) { diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index b546199d22..6f486872bd 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -117,6 +117,13 @@ class CommitSkipPatternsTest extends TestCase $this->assertTrue($validator->isValid('')); } + public function testNonStringCommitMessageNeverSkips(): void + { + $validator = new CommitSkipPatterns(['[skip deploy]']); + $this->assertTrue($validator->isValid(null)); + $this->assertTrue($validator->isValid([])); + } + public function testBlankPatternsInArrayAreIgnored(): void { $validator = new CommitSkipPatterns(['', ' ', '[skip deploy]']); From 9a6d2a6d2112f63eec3e7313e594d3c2b8af6039 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 18:02:51 +0530 Subject: [PATCH 06/15] refactor: use fixed commit skip directives --- app/config/collections/projects.php | 22 -- .../Functions/Http/Functions/Create.php | 3 - .../Functions/Http/Functions/Update.php | 3 - .../Modules/Sites/Http/Sites/Create.php | 4 - .../Modules/Sites/Http/Sites/Update.php | 5 - .../Modules/VCS/Http/GitHub/Deployment.php | 6 +- src/Appwrite/Utopia/Response/Model/Func.php | 7 - src/Appwrite/Utopia/Response/Model/Site.php | 7 - .../Vcs/Validator/CommitSkipPatterns.php | 29 ++- .../Vcs/Validator/CommitSkipPatternsTest.php | 227 ++++-------------- 10 files changed, 62 insertions(+), 251 deletions(-) diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index 0f1bfe12f5..9568c59369 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -841,17 +841,6 @@ return [ 'array' => true, 'filters' => [], ], - [ - '$id' => ID::custom('providerCommitSkipPatterns'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => true, - 'filters' => [], - ], ], 'indexes' => [ [ @@ -1331,17 +1320,6 @@ return [ 'array' => false, 'filters' => [], ], - [ - '$id' => ID::custom('providerCommitSkipPatterns'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 128, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => true, - 'filters' => [], - ], ], 'indexes' => [ [ diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index b9bd67b1e0..00a91141fb 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -94,7 +94,6 @@ class Create extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) - ->param('providerCommitSkipPatterns', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -147,7 +146,6 @@ class Create extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, - array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, string $templateRepository, @@ -249,7 +247,6 @@ class Create extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, - 'providerCommitSkipPatterns' => $providerCommitSkipPatterns, 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, ])); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index 39b56bb39e..b3fcb2c021 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -87,7 +87,6 @@ class Update extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true) - ->param('providerCommitSkipPatterns', null, new Nullable(new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -133,7 +132,6 @@ class Update extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, - ?array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -278,7 +276,6 @@ class Update extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, - 'providerCommitSkipPatterns' => $providerCommitSkipPatterns ?? $function->getAttribute('providerCommitSkipPatterns', []), 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'search' => implode(' ', [$functionId, $name, $runtime]), diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php index 95c34ce686..d01d0d8ca7 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php @@ -19,7 +19,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; -use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Range; use Utopia\Validator\Text; @@ -79,7 +78,6 @@ class Create extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true) - ->param('providerCommitSkipPatterns', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -120,7 +118,6 @@ class Create extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, - array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -176,7 +173,6 @@ class Create extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, - 'providerCommitSkipPatterns' => $providerCommitSkipPatterns, 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'buildRuntime' => $buildRuntime, diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 9e5184abd0..2aee03265e 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -22,9 +22,7 @@ use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; -use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; -use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -83,7 +81,6 @@ class Update extends Base ->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true) ->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true) ->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true) - ->param('providerCommitSkipPatterns', null, new Nullable(new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'List of commit message patterns to skip automatic deployments. Leave empty to deploy on all commits.', true) ->param('buildSpecification', fn (array $plan) => $this->getDefaultSpecification($plan), fn (array $plan) => new Specification( $plan, Config::getParam('specifications', []), @@ -129,7 +126,6 @@ class Update extends Base string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, - ?array $providerCommitSkipPatterns, string $buildSpecification, string $runtimeSpecification, int $deploymentRetention, @@ -275,7 +271,6 @@ class Update extends Base 'providerBranch' => $providerBranch, 'providerRootDirectory' => $providerRootDirectory, 'providerSilentMode' => $providerSilentMode, - 'providerCommitSkipPatterns' => $providerCommitSkipPatterns ?? $site->getAttribute('providerCommitSkipPatterns', []), 'buildSpecification' => $buildSpecification, 'runtimeSpecification' => $runtimeSpecification, 'search' => implode(' ', [$siteId, $name, $framework]), diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index a6124412bb..f244dd1829 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -96,7 +96,7 @@ trait Deployment $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); - if (!$this->isResourceBuildable($resource, $logBase, $providerCommitMessage)) { + if (!$this->isResourceBuildable($logBase, $providerCommitMessage)) { Span::add("{$logBase}.build.skipped", 'true'); continue; } @@ -568,9 +568,9 @@ trait Deployment return System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME); } - private function isResourceBuildable(Document $resource, string $logBase, string $providerCommitMessage = ''): bool + private function isResourceBuildable(string $logBase, string $providerCommitMessage = ''): bool { - $commitSkip = new CommitSkipPatterns($resource->getAttribute('providerCommitSkipPatterns', [])); + $commitSkip = new CommitSkipPatterns(); if (!$commitSkip->isValid($providerCommitMessage)) { Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); return false; diff --git a/src/Appwrite/Utopia/Response/Model/Func.php b/src/Appwrite/Utopia/Response/Model/Func.php index a85014b9d8..3aea364fe5 100644 --- a/src/Appwrite/Utopia/Response/Model/Func.php +++ b/src/Appwrite/Utopia/Response/Model/Func.php @@ -182,13 +182,6 @@ class Func extends Model 'default' => false, 'example' => false, ]) - ->addRule('providerCommitSkipPatterns', [ - 'type' => self::TYPE_STRING, - 'description' => 'Commit message patterns that skip automatic deployments', - 'default' => [], - 'example' => ['[skip deploy]'], - 'array' => true, - ]) ->addRule('buildSpecification', [ 'type' => self::TYPE_STRING, 'description' => 'Machine specification for deployment builds.', diff --git a/src/Appwrite/Utopia/Response/Model/Site.php b/src/Appwrite/Utopia/Response/Model/Site.php index 8bb33f6000..941b6104df 100644 --- a/src/Appwrite/Utopia/Response/Model/Site.php +++ b/src/Appwrite/Utopia/Response/Model/Site.php @@ -173,13 +173,6 @@ class Site extends Model 'default' => false, 'example' => false, ]) - ->addRule('providerCommitSkipPatterns', [ - 'type' => self::TYPE_STRING, - 'description' => 'Commit message patterns that skip automatic deployments', - 'default' => [], - 'example' => ['[skip deploy]'], - 'array' => true, - ]) ->addRule('buildSpecification', [ 'type' => self::TYPE_STRING, 'description' => 'Machine specification for deployment builds.', diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index fc5dec2de3..8439be4dd5 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -6,14 +6,24 @@ use Utopia\Validator; class CommitSkipPatterns extends Validator { - public function __construct(private readonly array $patterns) - { - } + private const PATTERNS = [ + '[skip ci]', + '[ci skip]', + '[no ci]', + '[skip actions]', + '[actions skip]', + '[skip deploy]', + '[deploy skip]', + '[no deploy]', + 'skip-checks: true', + 'skip appwrite', + 'appwrite skip', + ]; /** * Returns false (skip deployment) when the commit message contains any of the - * configured patterns as a standalone directive (case-insensitive). - * Returns true (proceed) when no patterns are configured or none match. + * known skip directives as a standalone directive (case-insensitive). + * Returns true (proceed) when none match. * * Matching rules: * - Case-insensitive @@ -29,15 +39,8 @@ class CommitSkipPatterns extends Validator return true; } - foreach ($this->patterns as $pattern) { - if (!is_string($pattern)) { - continue; - } - + foreach (self::PATTERNS as $pattern) { $pattern = trim($pattern); - if ($pattern === '') { - continue; - } // Split on whitespace; each token is regex-quoted. Tokens are rejoined // with \s+ (required space) so that "skipappwrite" does NOT match the diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index 6f486872bd..3be02e93c9 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -7,216 +7,75 @@ use PHPUnit\Framework\TestCase; class CommitSkipPatternsTest extends TestCase { - // ------------------------------------------------------------------------- - // Empty patterns — never skip - // ------------------------------------------------------------------------- - - public function testEmptyPatternsNeverSkip(): void + public function testKnownSkipDirectivesSkip(): void { - $validator = new CommitSkipPatterns([]); - $this->assertTrue($validator->isValid('fix: update readme')); - $this->assertTrue($validator->isValid('[skip deploy] docs only')); - $this->assertTrue($validator->isValid('')); + $validator = new CommitSkipPatterns(); + + $this->assertFalse($validator->isValid('[skip ci] update changelog')); + $this->assertFalse($validator->isValid('[ci skip] update changelog')); + $this->assertFalse($validator->isValid('[no ci] update changelog')); + $this->assertFalse($validator->isValid('[skip actions] update changelog')); + $this->assertFalse($validator->isValid('[actions skip] update changelog')); + $this->assertFalse($validator->isValid('[skip deploy] update changelog')); + $this->assertFalse($validator->isValid('[deploy skip] update changelog')); + $this->assertFalse($validator->isValid('[no deploy] update changelog')); + $this->assertFalse($validator->isValid('skip-checks:true')); + $this->assertFalse($validator->isValid('skip appwrite')); + $this->assertFalse($validator->isValid('appwrite skip')); } - // ------------------------------------------------------------------------- - // Single pattern — directive match - // ------------------------------------------------------------------------- - - public function testSinglePatternMatchSkips(): void + public function testKnownSkipDirectivesAreCaseInsensitive(): void { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $this->assertFalse($validator->isValid('[skip deploy] docs only')); - $this->assertFalse($validator->isValid('chore: update deps [skip deploy]')); - $this->assertFalse($validator->isValid('prefix [skip deploy] suffix')); + $validator = new CommitSkipPatterns(); + + $this->assertFalse($validator->isValid('[SKIP CI] update changelog')); + $this->assertFalse($validator->isValid('[Skip Deploy] update changelog')); + $this->assertFalse($validator->isValid('SKIP APPWRITE')); + $this->assertFalse($validator->isValid('Appwrite Skip')); } - public function testSinglePatternNoMatchProceeds(): void + public function testMessageWithoutKnownDirectiveProceeds(): void { - $validator = new CommitSkipPatterns(['[skip deploy]']); + $validator = new CommitSkipPatterns(); + $this->assertTrue($validator->isValid('fix: real bug fix')); $this->assertTrue($validator->isValid('feat: add new feature')); $this->assertTrue($validator->isValid('skip deploy without brackets')); - $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); - } - - // ------------------------------------------------------------------------- - // Case insensitivity - // ------------------------------------------------------------------------- - - public function testCaseInsensitiveMatch(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $this->assertFalse($validator->isValid('[SKIP DEPLOY] uppercase')); - $this->assertFalse($validator->isValid('[Skip Deploy] mixed case')); - $this->assertFalse($validator->isValid('[skip DEPLOY] partial upper')); - } - - public function testPatternItselfCaseInsensitive(): void - { - $validator = new CommitSkipPatterns(['[SKIP DEPLOY]']); - $this->assertFalse($validator->isValid('[skip deploy] lowercase message')); - $this->assertFalse($validator->isValid('[Skip Deploy] mixed message')); - } - - // ------------------------------------------------------------------------- - // Array of patterns — any match skips (OR semantics) - // ------------------------------------------------------------------------- - - public function testMultiplePatternsFirstMatches(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); - $this->assertFalse($validator->isValid('[skip deploy] docs only')); - } - - public function testMultiplePatternsSecondMatches(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); - $this->assertFalse($validator->isValid('chore: update readme [skip ci]')); - } - - public function testMultiplePatternsThirdMatches(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); - $this->assertFalse($validator->isValid('[no deploy] just docs')); - } - - public function testMultiplePatternsNoneMatchProceeds(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]', '[skip ci]', '[no deploy]']); - $this->assertTrue($validator->isValid('feat: completely new feature')); - $this->assertTrue($validator->isValid('fix: important bug fix')); - } - - // ------------------------------------------------------------------------- - // Common real-world skip conventions - // ------------------------------------------------------------------------- - - public function testCommonSkipCiPattern(): void - { - $validator = new CommitSkipPatterns(['[skip ci]']); - $this->assertFalse($validator->isValid('[skip ci] update changelog')); - $this->assertFalse($validator->isValid('[SKIP CI]')); - $this->assertTrue($validator->isValid('feat: something real')); - } - - public function testNoDeployPattern(): void - { - $validator = new CommitSkipPatterns(['[no deploy]']); - $this->assertFalse($validator->isValid('[no deploy] tweak docs')); $this->assertTrue($validator->isValid('deploy this please')); } - // ------------------------------------------------------------------------- - // Edge cases - // ------------------------------------------------------------------------- - - public function testEmptyCommitMessageNeverSkipsWithPatterns(): void + public function testDirectiveMustBeStandalone(): void { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $this->assertTrue($validator->isValid('')); - } + $validator = new CommitSkipPatterns(); - public function testNonStringCommitMessageNeverSkips(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $this->assertTrue($validator->isValid(null)); - $this->assertTrue($validator->isValid([])); - } - - public function testBlankPatternsInArrayAreIgnored(): void - { - $validator = new CommitSkipPatterns(['', ' ', '[skip deploy]']); - $this->assertTrue($validator->isValid('normal commit message')); - $this->assertFalse($validator->isValid('[skip deploy] docs')); - } - - public function testPatternMustBeStandaloneDirective(): void - { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $this->assertTrue($validator->isValid('skippy the kangaroo')); + $this->assertFalse($validator->isValid('docs: update readme [skip deploy]')); + $this->assertTrue($validator->isValid('docs: update readme[skip deploy]')); $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); + $this->assertTrue($validator->isValid('skipappwrite')); + $this->assertTrue($validator->isValid('appwriteskip')); } - public function testMultilineCommitMessage(): void + public function testMultilineCommitMessageSkips(): void { - $validator = new CommitSkipPatterns(['[skip deploy]']); - $msg = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; - $this->assertFalse($validator->isValid($msg)); + $validator = new CommitSkipPatterns(); + $message = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; + + $this->assertFalse($validator->isValid($message)); } public function testWhitespaceInsideDirectiveIsNormalized(): void { - $validator = new CommitSkipPatterns([' [skip deploy] ']); - $this->assertFalse($validator->isValid('[skip deploy] docs only')); - $this->assertFalse($validator->isValid('[SKIP DEPLOY] docs only')); + $validator = new CommitSkipPatterns(); + + $this->assertFalse($validator->isValid('[skip deploy] docs only')); + $this->assertFalse($validator->isValid('skip-checks: true')); } - public function testTrailerDirectiveCanSkip(): void + public function testNonStringCommitMessageProceeds(): void { - $validator = new CommitSkipPatterns(['skip-checks: true']); - $msg = "feat: add new stuff\n\nMore detail here.\n\nskip-checks:true"; - $this->assertFalse($validator->isValid($msg)); - } + $validator = new CommitSkipPatterns(); - // ------------------------------------------------------------------------- - // Plain-word patterns: "skip appwrite" and "appwrite skip" - // ------------------------------------------------------------------------- - - public function testSkipAppwritePatternSkips(): void - { - $validator = new CommitSkipPatterns(['skip appwrite']); - $this->assertFalse($validator->isValid('docs: update readme skip appwrite')); - $this->assertFalse($validator->isValid('skip appwrite')); - } - - public function testSkipAppwritePatternCaseInsensitive(): void - { - $validator = new CommitSkipPatterns(['skip appwrite']); - $this->assertFalse($validator->isValid('SKIP APPWRITE')); - $this->assertFalse($validator->isValid('Skip Appwrite')); - $this->assertFalse($validator->isValid('SKIP appwrite')); - } - - public function testSkipAppwritePatternNoMatchProceeds(): void - { - $validator = new CommitSkipPatterns(['skip appwrite']); - $this->assertTrue($validator->isValid('feat: real feature')); - $this->assertTrue($validator->isValid('skipappwrite')); // no space — not standalone - $this->assertTrue($validator->isValid('appwrite is great')); - } - - public function testAppwriteSkipPatternSkips(): void - { - $validator = new CommitSkipPatterns(['appwrite skip']); - $this->assertFalse($validator->isValid('appwrite skip ci')); - $this->assertFalse($validator->isValid('docs appwrite skip')); - $this->assertFalse($validator->isValid('appwrite skip')); - } - - public function testAppwriteSkipPatternCaseInsensitive(): void - { - $validator = new CommitSkipPatterns(['appwrite skip']); - $this->assertFalse($validator->isValid('APPWRITE SKIP')); - $this->assertFalse($validator->isValid('Appwrite Skip')); - $this->assertFalse($validator->isValid('appwrite SKIP')); - } - - public function testAppwriteSkipPatternNoMatchProceeds(): void - { - $validator = new CommitSkipPatterns(['appwrite skip']); - $this->assertTrue($validator->isValid('feat: deploy appwrite changes')); - $this->assertTrue($validator->isValid('appwriteskip')); // no space — not standalone - $this->assertTrue($validator->isValid('skip the appwrite stuff')); - } - - public function testBothAppwritePatternsInArray(): void - { - $validator = new CommitSkipPatterns(['skip appwrite', 'appwrite skip']); - $this->assertFalse($validator->isValid('skip appwrite')); - $this->assertFalse($validator->isValid('appwrite skip')); - $this->assertFalse($validator->isValid('SKIP APPWRITE')); - $this->assertFalse($validator->isValid('APPWRITE SKIP')); - $this->assertTrue($validator->isValid('feat: deploy appwrite changes')); + $this->assertTrue($validator->isValid(null)); + $this->assertTrue($validator->isValid([])); } } From fa63983fd7695783f0d371ad31cb564278a01886 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 18:11:48 +0530 Subject: [PATCH 07/15] fix: bracket appwrite skip directives --- src/Appwrite/Vcs/Validator/CommitSkipPatterns.php | 4 ++-- tests/unit/Vcs/Validator/CommitSkipPatternsTest.php | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index 8439be4dd5..e5988555eb 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -16,8 +16,8 @@ class CommitSkipPatterns extends Validator '[deploy skip]', '[no deploy]', 'skip-checks: true', - 'skip appwrite', - 'appwrite skip', + '[skip appwrite]', + '[appwrite skip]', ]; /** diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index 3be02e93c9..74c401230e 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -20,8 +20,8 @@ class CommitSkipPatternsTest extends TestCase $this->assertFalse($validator->isValid('[deploy skip] update changelog')); $this->assertFalse($validator->isValid('[no deploy] update changelog')); $this->assertFalse($validator->isValid('skip-checks:true')); - $this->assertFalse($validator->isValid('skip appwrite')); - $this->assertFalse($validator->isValid('appwrite skip')); + $this->assertFalse($validator->isValid('[skip appwrite] update changelog')); + $this->assertFalse($validator->isValid('[appwrite skip] update changelog')); } public function testKnownSkipDirectivesAreCaseInsensitive(): void @@ -30,8 +30,8 @@ class CommitSkipPatternsTest extends TestCase $this->assertFalse($validator->isValid('[SKIP CI] update changelog')); $this->assertFalse($validator->isValid('[Skip Deploy] update changelog')); - $this->assertFalse($validator->isValid('SKIP APPWRITE')); - $this->assertFalse($validator->isValid('Appwrite Skip')); + $this->assertFalse($validator->isValid('[SKIP APPWRITE] update changelog')); + $this->assertFalse($validator->isValid('[Appwrite Skip] update changelog')); } public function testMessageWithoutKnownDirectiveProceeds(): void @@ -51,8 +51,8 @@ class CommitSkipPatternsTest extends TestCase $this->assertFalse($validator->isValid('docs: update readme [skip deploy]')); $this->assertTrue($validator->isValid('docs: update readme[skip deploy]')); $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); - $this->assertTrue($validator->isValid('skipappwrite')); - $this->assertTrue($validator->isValid('appwriteskip')); + $this->assertTrue($validator->isValid('refactor: skip appwrite cache seeding')); + $this->assertTrue($validator->isValid('fix: appwrite skip quota check in tests')); } public function testMultilineCommitMessageSkips(): void From cc0207cfe4c5e8d43f0277e74efa8c21a431c571 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 18:32:14 +0530 Subject: [PATCH 08/15] fix: align appwrite skip directives --- .../Vcs/Validator/CommitSkipPatterns.php | 16 +++++++++------- .../Vcs/Validator/CommitSkipPatternsTest.php | 10 ++++++++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php index e5988555eb..75f0bdd9c0 100644 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php @@ -10,14 +10,18 @@ class CommitSkipPatterns extends Validator '[skip ci]', '[ci skip]', '[no ci]', + '[skip action]', + '[action skip]', + '[no action]', '[skip actions]', '[actions skip]', + '[no actions]', '[skip deploy]', '[deploy skip]', '[no deploy]', - 'skip-checks: true', '[skip appwrite]', '[appwrite skip]', + '[no appwrite]', ]; /** @@ -30,8 +34,8 @@ class CommitSkipPatterns extends Validator * - The directive must be surrounded by whitespace or string boundaries, so * "prefix[skip deploy]suffix" does NOT accidentally skip * - Internal whitespace in the pattern is normalised: tokens are split on \s+ - * and rejoined with \s* in the regex, so "[skip deploy]" matches - * "[skip deploy]" and "skip-checks: true" matches "skip-checks:true" + * and rejoined with \s+ in the regex, so "[skip deploy]" matches + * "[skip deploy]". */ public function isValid($value): bool { @@ -44,16 +48,14 @@ class CommitSkipPatterns extends Validator // Split on whitespace; each token is regex-quoted. Tokens are rejoined // with \s+ (required space) so that "skipappwrite" does NOT match the - // pattern "skip appwrite". The only exception: when the preceding token - // ends with ":" (git trailer style), \s* is used so that - // "skip-checks:true" still matches the pattern "skip-checks: true". + // pattern "skip appwrite". $tokens = preg_split('/\s+/', $pattern); $regexParts = []; $count = count($tokens); for ($i = 0; $i < $count; $i++) { $regexParts[] = preg_quote($tokens[$i], '~'); if ($i < $count - 1) { - $regexParts[] = str_ends_with($tokens[$i], ':') ? '\s*' : '\s+'; + $regexParts[] = '\s+'; } } $regexBody = implode('', $regexParts); diff --git a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php index 74c401230e..64f5140db7 100644 --- a/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/CommitSkipPatternsTest.php @@ -14,14 +14,18 @@ class CommitSkipPatternsTest extends TestCase $this->assertFalse($validator->isValid('[skip ci] update changelog')); $this->assertFalse($validator->isValid('[ci skip] update changelog')); $this->assertFalse($validator->isValid('[no ci] update changelog')); + $this->assertFalse($validator->isValid('[skip action] update changelog')); + $this->assertFalse($validator->isValid('[action skip] update changelog')); + $this->assertFalse($validator->isValid('[no action] update changelog')); $this->assertFalse($validator->isValid('[skip actions] update changelog')); $this->assertFalse($validator->isValid('[actions skip] update changelog')); + $this->assertFalse($validator->isValid('[no actions] update changelog')); $this->assertFalse($validator->isValid('[skip deploy] update changelog')); $this->assertFalse($validator->isValid('[deploy skip] update changelog')); $this->assertFalse($validator->isValid('[no deploy] update changelog')); - $this->assertFalse($validator->isValid('skip-checks:true')); $this->assertFalse($validator->isValid('[skip appwrite] update changelog')); $this->assertFalse($validator->isValid('[appwrite skip] update changelog')); + $this->assertFalse($validator->isValid('[no appwrite] update changelog')); } public function testKnownSkipDirectivesAreCaseInsensitive(): void @@ -32,6 +36,7 @@ class CommitSkipPatternsTest extends TestCase $this->assertFalse($validator->isValid('[Skip Deploy] update changelog')); $this->assertFalse($validator->isValid('[SKIP APPWRITE] update changelog')); $this->assertFalse($validator->isValid('[Appwrite Skip] update changelog')); + $this->assertFalse($validator->isValid('[No Actions] update changelog')); } public function testMessageWithoutKnownDirectiveProceeds(): void @@ -42,6 +47,7 @@ class CommitSkipPatternsTest extends TestCase $this->assertTrue($validator->isValid('feat: add new feature')); $this->assertTrue($validator->isValid('skip deploy without brackets')); $this->assertTrue($validator->isValid('deploy this please')); + $this->assertTrue($validator->isValid('skip-checks:true')); } public function testDirectiveMustBeStandalone(): void @@ -68,7 +74,7 @@ class CommitSkipPatternsTest extends TestCase $validator = new CommitSkipPatterns(); $this->assertFalse($validator->isValid('[skip deploy] docs only')); - $this->assertFalse($validator->isValid('skip-checks: true')); + $this->assertFalse($validator->isValid('[no actions] docs only')); } public function testNonStringCommitMessageProceeds(): void From dda38442ad76f60dde355fdec56ded980c79fa41 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 11 May 2026 18:56:31 +0530 Subject: [PATCH 09/15] refactor: simplify deployment skip patterns --- .../Modules/VCS/Http/GitHub/Deployment.php | 16 +--- .../Vcs/Validator/CommitSkipPatterns.php | 90 ------------------- .../Vcs/Validator/DeploymentSkipPatterns.php | 66 ++++++++++++++ ...est.php => DeploymentSkipPatternsTest.php} | 30 +++---- 4 files changed, 81 insertions(+), 121 deletions(-) delete mode 100644 src/Appwrite/Vcs/Validator/CommitSkipPatterns.php create mode 100644 src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php rename tests/unit/Vcs/Validator/{CommitSkipPatternsTest.php => DeploymentSkipPatternsTest.php} (77%) diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index f244dd1829..22920e7679 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -8,7 +8,7 @@ use Appwrite\Event\Publisher\Build as BuildPublisher; use Appwrite\Extend\Exception; use Appwrite\Filter\BranchDomain as BranchDomainFilter; use Appwrite\Vcs\Comment; -use Appwrite\Vcs\Validator\CommitSkipPatterns; +use Appwrite\Vcs\Validator\DeploymentSkipPatterns; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -96,7 +96,9 @@ trait Deployment $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); - if (!$this->isResourceBuildable($logBase, $providerCommitMessage)) { + $commitSkip = new DeploymentSkipPatterns(); + if (!$commitSkip->isValid($providerCommitMessage)) { + Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); Span::add("{$logBase}.build.skipped", 'true'); continue; } @@ -568,14 +570,4 @@ trait Deployment return System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME); } - private function isResourceBuildable(string $logBase, string $providerCommitMessage = ''): bool - { - $commitSkip = new CommitSkipPatterns(); - if (!$commitSkip->isValid($providerCommitMessage)) { - Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); - return false; - } - - return true; - } } diff --git a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php b/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php deleted file mode 100644 index 75f0bdd9c0..0000000000 --- a/src/Appwrite/Vcs/Validator/CommitSkipPatterns.php +++ /dev/null @@ -1,90 +0,0 @@ -assertFalse($validator->isValid('[skip ci] update changelog')); $this->assertFalse($validator->isValid('[ci skip] update changelog')); @@ -30,7 +30,7 @@ class CommitSkipPatternsTest extends TestCase public function testKnownSkipDirectivesAreCaseInsensitive(): void { - $validator = new CommitSkipPatterns(); + $validator = new DeploymentSkipPatterns(); $this->assertFalse($validator->isValid('[SKIP CI] update changelog')); $this->assertFalse($validator->isValid('[Skip Deploy] update changelog')); @@ -41,7 +41,7 @@ class CommitSkipPatternsTest extends TestCase public function testMessageWithoutKnownDirectiveProceeds(): void { - $validator = new CommitSkipPatterns(); + $validator = new DeploymentSkipPatterns(); $this->assertTrue($validator->isValid('fix: real bug fix')); $this->assertTrue($validator->isValid('feat: add new feature')); @@ -50,36 +50,28 @@ class CommitSkipPatternsTest extends TestCase $this->assertTrue($validator->isValid('skip-checks:true')); } - public function testDirectiveMustBeStandalone(): void + public function testDirectiveCanAppearAnywhere(): void { - $validator = new CommitSkipPatterns(); + $validator = new DeploymentSkipPatterns(); $this->assertFalse($validator->isValid('docs: update readme [skip deploy]')); - $this->assertTrue($validator->isValid('docs: update readme[skip deploy]')); - $this->assertTrue($validator->isValid('prefix[skip deploy]suffix')); + $this->assertFalse($validator->isValid('docs: update readme[skip deploy]')); + $this->assertFalse($validator->isValid('prefix[skip deploy]suffix')); $this->assertTrue($validator->isValid('refactor: skip appwrite cache seeding')); $this->assertTrue($validator->isValid('fix: appwrite skip quota check in tests')); } public function testMultilineCommitMessageSkips(): void { - $validator = new CommitSkipPatterns(); + $validator = new DeploymentSkipPatterns(); $message = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; $this->assertFalse($validator->isValid($message)); } - public function testWhitespaceInsideDirectiveIsNormalized(): void - { - $validator = new CommitSkipPatterns(); - - $this->assertFalse($validator->isValid('[skip deploy] docs only')); - $this->assertFalse($validator->isValid('[no actions] docs only')); - } - public function testNonStringCommitMessageProceeds(): void { - $validator = new CommitSkipPatterns(); + $validator = new DeploymentSkipPatterns(); $this->assertTrue($validator->isValid(null)); $this->assertTrue($validator->isValid([])); From e3755679c519ed61c37ae14651313d226e73a94e Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 17:26:32 +0530 Subject: [PATCH 10/15] refactor: extend Contains validator, keep only [skip ci] pattern --- composer.json | 2 +- composer.lock | 14 ++-- .../Modules/VCS/Http/GitHub/Deployment.php | 2 +- .../Vcs/Validator/DeploymentSkipPatterns.php | 57 ++-------------- .../Validator/DeploymentSkipPatternsTest.php | 67 ++++++------------- 5 files changed, 35 insertions(+), 107 deletions(-) diff --git a/composer.json b/composer.json index 7d68a838f8..5eb68b6199 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ "utopia-php/dsn": "0.2.1", "utopia-php/http": "0.34.*", "utopia-php/fetch": "^1.1", - "utopia-php/validators": "0.2.*", + "utopia-php/validators": "0.2.3", "utopia-php/image": "0.8.*", "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.8.*", diff --git a/composer.lock b/composer.lock index 0aa58ca03e..d06d9ce12b 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": "d58736fec3028d1f9aedd055e6d82684", + "content-hash": "52bfeef1fecc435484005e472d15d21a", "packages": [ { "name": "adhocore/jwt", @@ -5193,16 +5193,16 @@ }, { "name": "utopia-php/validators", - "version": "0.2.2", + "version": "0.2.3", "source": { "type": "git", "url": "https://github.com/utopia-php/validators.git", - "reference": "5d7d494e64457cd4eb67fdcfd9481f2c89796aa6" + "reference": "9770269c8ed8e6909934965fa8722103c7434c23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/validators/zipball/5d7d494e64457cd4eb67fdcfd9481f2c89796aa6", - "reference": "5d7d494e64457cd4eb67fdcfd9481f2c89796aa6", + "url": "https://api.github.com/repos/utopia-php/validators/zipball/9770269c8ed8e6909934965fa8722103c7434c23", + "reference": "9770269c8ed8e6909934965fa8722103c7434c23", "shasum": "" }, "require": { @@ -5232,9 +5232,9 @@ ], "support": { "issues": "https://github.com/utopia-php/validators/issues", - "source": "https://github.com/utopia-php/validators/tree/0.2.2" + "source": "https://github.com/utopia-php/validators/tree/0.2.3" }, - "time": "2026-04-27T16:30:24+00:00" + "time": "2026-05-14T08:05:44+00:00" }, { "name": "utopia-php/vcs", diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index 22920e7679..6428708a75 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -97,7 +97,7 @@ trait Deployment $resourceInternalId = $resource->getSequence(); $commitSkip = new DeploymentSkipPatterns(); - if (!$commitSkip->isValid($providerCommitMessage)) { + if ($commitSkip->isValid($providerCommitMessage)) { Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); Span::add("{$logBase}.build.skipped", 'true'); continue; diff --git a/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php b/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php index 8810ed3912..8e1d0623d9 100644 --- a/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php +++ b/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php @@ -2,65 +2,16 @@ namespace Appwrite\Vcs\Validator; -use Utopia\Validator; +use Utopia\Validator\Contains; -class DeploymentSkipPatterns extends Validator +class DeploymentSkipPatterns extends Contains { private const PATTERNS = [ '[skip ci]', - '[ci skip]', - '[no ci]', - '[skip action]', - '[action skip]', - '[no action]', - '[skip actions]', - '[actions skip]', - '[no actions]', - '[skip deploy]', - '[deploy skip]', - '[no deploy]', - '[skip appwrite]', - '[appwrite skip]', - '[no appwrite]', ]; - /** - * Returns false (skip deployment) when the commit message contains any of the - * known skip directives as a standalone directive (case-insensitive). - * Returns true (proceed) when none match. - * - * Matching rules: - * - Case-insensitive - */ - public function isValid($value): bool + public function __construct() { - if (!is_string($value)) { - return true; - } - - $value = strtolower($value); - - foreach (self::PATTERNS as $pattern) { - if (str_contains($value, $pattern)) { - return false; - } - } - - return true; - } - - public function getDescription(): string - { - return 'Commit message must not contain any of the configured skip patterns.'; - } - - public function isArray(): bool - { - return false; - } - - public function getType(): string - { - return self::TYPE_STRING; + parent::__construct(self::PATTERNS); } } diff --git a/tests/unit/Vcs/Validator/DeploymentSkipPatternsTest.php b/tests/unit/Vcs/Validator/DeploymentSkipPatternsTest.php index 0882d781c0..473ba4c6f4 100644 --- a/tests/unit/Vcs/Validator/DeploymentSkipPatternsTest.php +++ b/tests/unit/Vcs/Validator/DeploymentSkipPatternsTest.php @@ -7,73 +7,50 @@ use PHPUnit\Framework\TestCase; class DeploymentSkipPatternsTest extends TestCase { + private DeploymentSkipPatterns $validator; + + protected function setUp(): void + { + $this->validator = new DeploymentSkipPatterns(); + } + public function testKnownSkipDirectivesSkip(): void { - $validator = new DeploymentSkipPatterns(); - - $this->assertFalse($validator->isValid('[skip ci] update changelog')); - $this->assertFalse($validator->isValid('[ci skip] update changelog')); - $this->assertFalse($validator->isValid('[no ci] update changelog')); - $this->assertFalse($validator->isValid('[skip action] update changelog')); - $this->assertFalse($validator->isValid('[action skip] update changelog')); - $this->assertFalse($validator->isValid('[no action] update changelog')); - $this->assertFalse($validator->isValid('[skip actions] update changelog')); - $this->assertFalse($validator->isValid('[actions skip] update changelog')); - $this->assertFalse($validator->isValid('[no actions] update changelog')); - $this->assertFalse($validator->isValid('[skip deploy] update changelog')); - $this->assertFalse($validator->isValid('[deploy skip] update changelog')); - $this->assertFalse($validator->isValid('[no deploy] update changelog')); - $this->assertFalse($validator->isValid('[skip appwrite] update changelog')); - $this->assertFalse($validator->isValid('[appwrite skip] update changelog')); - $this->assertFalse($validator->isValid('[no appwrite] update changelog')); + $this->assertTrue($this->validator->isValid('[skip ci] update changelog')); } public function testKnownSkipDirectivesAreCaseInsensitive(): void { - $validator = new DeploymentSkipPatterns(); - - $this->assertFalse($validator->isValid('[SKIP CI] update changelog')); - $this->assertFalse($validator->isValid('[Skip Deploy] update changelog')); - $this->assertFalse($validator->isValid('[SKIP APPWRITE] update changelog')); - $this->assertFalse($validator->isValid('[Appwrite Skip] update changelog')); - $this->assertFalse($validator->isValid('[No Actions] update changelog')); + $this->assertTrue($this->validator->isValid('[SKIP CI] update changelog')); } public function testMessageWithoutKnownDirectiveProceeds(): void { - $validator = new DeploymentSkipPatterns(); - - $this->assertTrue($validator->isValid('fix: real bug fix')); - $this->assertTrue($validator->isValid('feat: add new feature')); - $this->assertTrue($validator->isValid('skip deploy without brackets')); - $this->assertTrue($validator->isValid('deploy this please')); - $this->assertTrue($validator->isValid('skip-checks:true')); + $this->assertFalse($this->validator->isValid('fix: real bug fix')); + $this->assertFalse($this->validator->isValid('feat: add new feature')); + $this->assertFalse($this->validator->isValid('skip deploy without brackets')); + $this->assertFalse($this->validator->isValid('deploy this please')); + $this->assertFalse($this->validator->isValid('skip-checks:true')); } public function testDirectiveCanAppearAnywhere(): void { - $validator = new DeploymentSkipPatterns(); - - $this->assertFalse($validator->isValid('docs: update readme [skip deploy]')); - $this->assertFalse($validator->isValid('docs: update readme[skip deploy]')); - $this->assertFalse($validator->isValid('prefix[skip deploy]suffix')); - $this->assertTrue($validator->isValid('refactor: skip appwrite cache seeding')); - $this->assertTrue($validator->isValid('fix: appwrite skip quota check in tests')); + $this->assertTrue($this->validator->isValid('docs: update readme [skip ci]')); + $this->assertTrue($this->validator->isValid('docs: update readme[skip ci]')); + $this->assertTrue($this->validator->isValid('prefix[skip ci]suffix')); + $this->assertFalse($this->validator->isValid('refactor: skip ci cache seeding')); } public function testMultilineCommitMessageSkips(): void { - $validator = new DeploymentSkipPatterns(); - $message = "feat: add new stuff\n\nMore detail here.\n\n[skip deploy]"; + $message = "feat: add new stuff\n\nMore detail here.\n\n[skip ci]"; - $this->assertFalse($validator->isValid($message)); + $this->assertTrue($this->validator->isValid($message)); } public function testNonStringCommitMessageProceeds(): void { - $validator = new DeploymentSkipPatterns(); - - $this->assertTrue($validator->isValid(null)); - $this->assertTrue($validator->isValid([])); + $this->assertFalse($this->validator->isValid(null)); + $this->assertFalse($this->validator->isValid([])); } } From 25772f4585217df2db035a7adb4a2eb18ff18a52 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 17:28:52 +0530 Subject: [PATCH 11/15] chore: composer update all dependencies --- composer.lock | 108 +++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/composer.lock b/composer.lock index d06d9ce12b..ccd69e927a 100644 --- a/composer.lock +++ b/composer.lock @@ -69,16 +69,16 @@ }, { "name": "appwrite/appwrite", - "version": "23.1.0", + "version": "23.1.1", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-for-php.git", - "reference": "2f275921f10ceb7cff99f2d463f7328b296234fa" + "reference": "fd7c0f0bf5ddf334533534b20ed967cfb400f6ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/2f275921f10ceb7cff99f2d463f7328b296234fa", - "reference": "2f275921f10ceb7cff99f2d463f7328b296234fa", + "url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/fd7c0f0bf5ddf334533534b20ed967cfb400f6ea", + "reference": "fd7c0f0bf5ddf334533534b20ed967cfb400f6ea", "shasum": "" }, "require": { @@ -104,10 +104,10 @@ "support": { "email": "team@appwrite.io", "issues": "https://github.com/appwrite/sdk-for-php/issues", - "source": "https://github.com/appwrite/sdk-for-php/tree/23.1.0", + "source": "https://github.com/appwrite/sdk-for-php/tree/23.1.1", "url": "https://appwrite.io/support" }, - "time": "2026-05-08T13:44:58+00:00" + "time": "2026-05-12T11:03:36+00:00" }, { "name": "appwrite/php-clamav", @@ -3614,16 +3614,16 @@ }, { "name": "utopia-php/cache", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "d36f9050c39c02e09a7763389c9e71258e74af1f" + "reference": "ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/d36f9050c39c02e09a7763389c9e71258e74af1f", - "reference": "d36f9050c39c02e09a7763389c9e71258e74af1f", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa", + "reference": "ef52a04e8bfa314c621e3d3326ffcf50db3dfdfa", "shasum": "" }, "require": { @@ -3660,9 +3660,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/1.0.2" + "source": "https://github.com/utopia-php/cache/tree/1.0.3" }, - "time": "2026-05-08T11:40:20+00:00" + "time": "2026-05-11T11:02:13+00:00" }, { "name": "utopia-php/cli", @@ -4490,16 +4490,16 @@ }, { "name": "utopia-php/messaging", - "version": "0.22.0", + "version": "0.22.2", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "a6ac04fd204fb6a16bf8c75a84d0b9fc10aa5030" + "reference": "f99feceab575243f3a86ee2e90cd1a6407805def" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a6ac04fd204fb6a16bf8c75a84d0b9fc10aa5030", - "reference": "a6ac04fd204fb6a16bf8c75a84d0b9fc10aa5030", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/f99feceab575243f3a86ee2e90cd1a6407805def", + "reference": "f99feceab575243f3a86ee2e90cd1a6407805def", "shasum": "" }, "require": { @@ -4535,22 +4535,22 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.22.0" + "source": "https://github.com/utopia-php/messaging/tree/0.22.2" }, - "time": "2026-04-02T04:09:19+00:00" + "time": "2026-05-14T08:51:26+00:00" }, { "name": "utopia-php/migration", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1" + "reference": "3ee6e12af256726bddc3a0402c94535132abecc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1", - "reference": "0fca44f40ad07bf2d56e9396afa6fa6d9b098ef1", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/3ee6e12af256726bddc3a0402c94535132abecc6", + "reference": "3ee6e12af256726bddc3a0402c94535132abecc6", "shasum": "" }, "require": { @@ -4590,9 +4590,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.11.0" + "source": "https://github.com/utopia-php/migration/tree/1.12.0" }, - "time": "2026-05-11T08:13:06+00:00" + "time": "2026-05-14T07:30:09+00:00" }, { "name": "utopia-php/mongo", @@ -5476,16 +5476,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.28.1", + "version": "1.29.2", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "009118ccda8ccece2b9fc043c158cb1dd3efaa88" + "reference": "31248a984a4d478d20a780dda8f5897984ee4e8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/009118ccda8ccece2b9fc043c158cb1dd3efaa88", - "reference": "009118ccda8ccece2b9fc043c158cb1dd3efaa88", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/31248a984a4d478d20a780dda8f5897984ee4e8f", + "reference": "31248a984a4d478d20a780dda8f5897984ee4e8f", "shasum": "" }, "require": { @@ -5521,9 +5521,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.28.1" + "source": "https://github.com/appwrite/sdk-generator/tree/1.29.2" }, - "time": "2026-05-08T13:24:33+00:00" + "time": "2026-05-13T04:47:38+00:00" }, { "name": "brianium/paratest", @@ -6630,16 +6630,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.5.24", + "version": "12.5.25", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046" + "reference": "792c2980442dfce319226b88fa845b8b6de3b333" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046", - "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/792c2980442dfce319226b88fa845b8b6de3b333", + "reference": "792c2980442dfce319226b88fa845b8b6de3b333", "shasum": "" }, "require": { @@ -6708,7 +6708,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.25" }, "funding": [ { @@ -6716,7 +6716,7 @@ "type": "other" } ], - "time": "2026-05-01T04:21:04+00:00" + "time": "2026-05-13T03:56:57+00:00" }, { "name": "sebastian/cli-parser", @@ -7701,16 +7701,16 @@ }, { "name": "symfony/console", - "version": "v8.0.9", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d" + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d", - "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "url": "https://api.github.com/repos/symfony/console/zipball/3156577f46a38aa1b9323aad223de7a9cd426782", + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782", "shasum": "" }, "require": { @@ -7767,7 +7767,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v8.0.9" + "source": "https://github.com/symfony/console/tree/v8.0.11" }, "funding": [ { @@ -7787,7 +7787,7 @@ "type": "tidelift" } ], - "time": "2026-04-29T15:02:55+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "symfony/polyfill-ctype", @@ -8121,16 +8121,16 @@ }, { "name": "symfony/process", - "version": "v8.0.8", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" + "reference": "26d89e459f037d2873300605d0a07e7a8ef84db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", - "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "url": "https://api.github.com/repos/symfony/process/zipball/26d89e459f037d2873300605d0a07e7a8ef84db0", + "reference": "26d89e459f037d2873300605d0a07e7a8ef84db0", "shasum": "" }, "require": { @@ -8162,7 +8162,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v8.0.8" + "source": "https://github.com/symfony/process/tree/v8.0.11" }, "funding": [ { @@ -8182,20 +8182,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-11T16:56:32+00:00" }, { "name": "symfony/string", - "version": "v8.0.8", + "version": "v8.0.11", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", - "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", + "url": "https://api.github.com/repos/symfony/string/zipball/39be2ad058a3c0bd558edca23e65f009865d75ff", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff", "shasum": "" }, "require": { @@ -8252,7 +8252,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.8" + "source": "https://github.com/symfony/string/tree/v8.0.11" }, "funding": [ { @@ -8272,7 +8272,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:14:47+00:00" + "time": "2026-05-13T12:07:53+00:00" }, { "name": "textalk/websocket", From 3cc600d29a21591996679657320a63d99d7f429f Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 17:56:09 +0530 Subject: [PATCH 12/15] fix: revert validators to 0.2.* for auto updates --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a3086822b3..7cd048726c 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ "utopia-php/dsn": "0.2.1", "utopia-php/http": "^2.0@RC", "utopia-php/fetch": "^1.1", - "utopia-php/validators": "0.2.3", + "utopia-php/validators": "0.2.*", "utopia-php/image": "0.8.*", "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.8.*", From b5f0ebb37b9cd29137695b4b0bf8533f46d30df7 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 17:59:34 +0530 Subject: [PATCH 13/15] refactor: replace DeploymentSkipPatterns validator with VCS_DEPLOYMENT_SKIP_PATTERNS constant --- app/init/constants.php | 5 ++ .../Modules/VCS/Http/GitHub/Deployment.php | 4 +- .../Vcs/Validator/DeploymentSkipPatterns.php | 17 ------ .../Validator/DeploymentSkipPatternsTest.php | 56 ------------------- 4 files changed, 7 insertions(+), 75 deletions(-) delete mode 100644 src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php delete mode 100644 tests/unit/Vcs/Validator/DeploymentSkipPatternsTest.php diff --git a/app/init/constants.php b/app/init/constants.php index abbe8a535e..5f3672bd45 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -514,3 +514,8 @@ const CSV_ALLOWED_DATABASE_TYPES = [ DATABASE_TYPE_TABLESDB, DATABASE_TYPE_VECTORSDB ]; + +// VCS deployment skip patterns +const VCS_DEPLOYMENT_SKIP_PATTERNS = [ + '[skip ci]', +]; diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index 811b83f722..bc6f4db7e9 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -8,8 +8,8 @@ use Appwrite\Event\Publisher\Build as BuildPublisher; use Appwrite\Extend\Exception; use Appwrite\Filter\BranchDomain as BranchDomainFilter; use Appwrite\Vcs\Comment; -use Appwrite\Vcs\Validator\DeploymentSkipPatterns; use Utopia\Config\Config; +use Utopia\Validator\Contains; use Utopia\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -96,7 +96,7 @@ trait Deployment $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); - $commitSkip = new DeploymentSkipPatterns(); + $commitSkip = new Contains(VCS_DEPLOYMENT_SKIP_PATTERNS); if ($commitSkip->isValid($providerCommitMessage)) { Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); Span::add("{$logBase}.build.skipped", 'true'); diff --git a/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php b/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php deleted file mode 100644 index 8e1d0623d9..0000000000 --- a/src/Appwrite/Vcs/Validator/DeploymentSkipPatterns.php +++ /dev/null @@ -1,17 +0,0 @@ -validator = new DeploymentSkipPatterns(); - } - - public function testKnownSkipDirectivesSkip(): void - { - $this->assertTrue($this->validator->isValid('[skip ci] update changelog')); - } - - public function testKnownSkipDirectivesAreCaseInsensitive(): void - { - $this->assertTrue($this->validator->isValid('[SKIP CI] update changelog')); - } - - public function testMessageWithoutKnownDirectiveProceeds(): void - { - $this->assertFalse($this->validator->isValid('fix: real bug fix')); - $this->assertFalse($this->validator->isValid('feat: add new feature')); - $this->assertFalse($this->validator->isValid('skip deploy without brackets')); - $this->assertFalse($this->validator->isValid('deploy this please')); - $this->assertFalse($this->validator->isValid('skip-checks:true')); - } - - public function testDirectiveCanAppearAnywhere(): void - { - $this->assertTrue($this->validator->isValid('docs: update readme [skip ci]')); - $this->assertTrue($this->validator->isValid('docs: update readme[skip ci]')); - $this->assertTrue($this->validator->isValid('prefix[skip ci]suffix')); - $this->assertFalse($this->validator->isValid('refactor: skip ci cache seeding')); - } - - public function testMultilineCommitMessageSkips(): void - { - $message = "feat: add new stuff\n\nMore detail here.\n\n[skip ci]"; - - $this->assertTrue($this->validator->isValid($message)); - } - - public function testNonStringCommitMessageProceeds(): void - { - $this->assertFalse($this->validator->isValid(null)); - $this->assertFalse($this->validator->isValid([])); - } -} From 0b073057758a351c7bde86e035ba719d3dc91b7a Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 18:05:04 +0530 Subject: [PATCH 14/15] fix: remove redundant comment, rename commitSkip to validator, use getDescription in span --- app/init/constants.php | 1 - .../Platform/Modules/VCS/Http/GitHub/Deployment.php | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/init/constants.php b/app/init/constants.php index 5f3672bd45..bdc8e67fae 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -515,7 +515,6 @@ const CSV_ALLOWED_DATABASE_TYPES = [ DATABASE_TYPE_VECTORSDB ]; -// VCS deployment skip patterns const VCS_DEPLOYMENT_SKIP_PATTERNS = [ '[skip ci]', ]; diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index bc6f4db7e9..38475b4df0 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -96,9 +96,9 @@ trait Deployment $resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId)); $resourceInternalId = $resource->getSequence(); - $commitSkip = new Contains(VCS_DEPLOYMENT_SKIP_PATTERNS); - if ($commitSkip->isValid($providerCommitMessage)) { - Span::add("{$logBase}.build.skipped.reason", 'commitMessage'); + $validator = new Contains(VCS_DEPLOYMENT_SKIP_PATTERNS); + if ($validator->isValid($providerCommitMessage)) { + Span::add("{$logBase}.build.skipped.reason", $validator->getDescription()); Span::add("{$logBase}.build.skipped", 'true'); continue; } From 437b3bfa10b7829872c9a26c6a013ba674891aa4 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Thu, 14 May 2026 18:08:59 +0530 Subject: [PATCH 15/15] fix: lint --- src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php index 38475b4df0..a6f0e7fd6d 100644 --- a/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php +++ b/src/Appwrite/Platform/Modules/VCS/Http/GitHub/Deployment.php @@ -9,7 +9,6 @@ use Appwrite\Extend\Exception; use Appwrite\Filter\BranchDomain as BranchDomainFilter; use Appwrite\Vcs\Comment; use Utopia\Config\Config; -use Utopia\Validator\Contains; use Utopia\Console; use Utopia\Database\Database; use Utopia\Database\Document; @@ -22,6 +21,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Span\Span; use Utopia\System\System; +use Utopia\Validator\Contains; use Utopia\VCS\Adapter\Git\GitHub; use Utopia\VCS\Exception\RepositoryNotFound;