$max) { throw InvalidWebhookPayloadException::forField($key); } return $value; } /** * An id that Coolify stores in or compares with an integer database * column, for example a pull request id or a repository project id. * * @throws InvalidWebhookPayloadException */ protected function webhookPayloadDatabaseId(mixed $payload, string $key, bool $required = false): ?int { return $this->webhookPayloadId($payload, $key, $required, self::WEBHOOK_MAX_DATABASE_INTEGER); } /** * A pull request or merge request id. The id is required. * * @throws InvalidWebhookPayloadException */ protected function webhookPullRequestId(mixed $payload, string $key): int { return $this->webhookPayloadDatabaseId($payload, $key, required: true); } /** * A commit SHA of the payload: 7 to 64 hexadecimal characters. A missing, * null or empty value gives null. * * @throws InvalidWebhookPayloadException When the value is not a commit SHA. */ protected function webhookCommitSha(mixed $payload, string $key): ?string { $value = data_get($payload, $key); if ($value === null || $value === '') { return null; } if (! is_string($value) || preg_match('/\A[0-9a-fA-F]{7,64}\z/', $value) !== 1) { throw InvalidWebhookPayloadException::forField($key); } return $value; } /** * An absolute http or https URL of the payload, for example a pull request * link that Coolify stores and shows in the UI. A missing, null or empty * value gives null. * * @throws InvalidWebhookPayloadException When the value is not a valid URL, or is missing and required. */ protected function webhookPayloadUrl(mixed $payload, string $key, bool $required = false): ?string { $value = $this->webhookPayloadString($payload, $key, $required); if ($value === null) { return null; } $scheme = parse_url($value, PHP_URL_SCHEME); $host = parse_url($value, PHP_URL_HOST); if ( strlen($value) > self::WEBHOOK_MAX_URL_LENGTH || preg_match('/[\s\x00-\x1F\x7F]/', $value) === 1 || ! is_string($scheme) || ! in_array(strtolower($scheme), ['http', 'https'], true) || ! is_string($host) || $host === '' ) { throw InvalidWebhookPayloadException::forField($key); } return $value; } /** * Files that the commits of a push add, remove or modify. * * Returns null when the payload has no commit list. The changed files are * then unknown, and watch paths must not skip the deployment. * * @return Collection|null */ protected function webhookPushChangedFiles(mixed $payload): ?Collection { $commits = data_get($payload, 'commits'); if (! is_array($commits)) { return null; } return collect($commits) ->filter(fn (mixed $commit): bool => is_array($commit)) ->flatMap(fn (array $commit): array => collect(['added', 'removed', 'modified']) ->flatMap(fn (string $type): array => is_array($commit[$type] ?? null) ? $commit[$type] : []) ->all()) ->filter(fn (mixed $file): bool => is_string($file) && $file !== '') ->unique() ->values(); } /** * Commit messages of a push. Values that are not strings are ignored. * * @return array */ protected function webhookPushCommitMessages(mixed $payload, string $commitsPath = 'commits'): array { $commits = data_get($payload, $commitsPath); if (! is_array($commits)) { return []; } return collect($commits) ->map(fn (mixed $commit): mixed => is_array($commit) ? ($commit['message'] ?? null) : null) ->filter(fn (mixed $message): bool => is_string($message)) ->values() ->all(); } /** * True when the push deletes the branch. Such a push has no commit to deploy. */ protected function isWebhookBranchDeletionPush(mixed $payload): bool { if (data_get($payload, 'deleted') === true) { return true; } $after = data_get($payload, 'after'); return is_string($after) && preg_match('/\A0+\z/', $after) === 1; } /** * True when the push must deploy the application with respect to its watch * paths. Unknown changed files (no commit list) do not skip the deployment. * * @param Collection|null $changedFiles */ protected function webhookPushMatchesWatchPaths(Application $application, ?Collection $changedFiles): bool { if (blank($application->watch_paths) || $changedFiles === null) { return true; } return $application->isWatchPathsTriggered($changedFiles); } }