diff --git a/inc/Loader/Git.php b/inc/Loader/Git.php index 796fadf..cb90316 100644 --- a/inc/Loader/Git.php +++ b/inc/Loader/Git.php @@ -51,8 +51,9 @@ public function download(): ?string { * @since 4.0.0 * * @param string $branch Name of the Git branch to clone. Empty string clones the default branch. + * @param \Required\Traduttore\Repository $repository Full repository instance. */ - $branch = apply_filters( 'traduttore.git_clone_branch', '' ); + $branch = apply_filters( 'traduttore.git_clone_branch', '', $this->repository ); if ( '' !== $branch ) { $cmd .= ' --branch ' . escapeshellarg( $branch ); } diff --git a/inc/WebhookHandler/Base.php b/inc/WebhookHandler/Base.php index f59f339..2a79c15 100644 --- a/inc/WebhookHandler/Base.php +++ b/inc/WebhookHandler/Base.php @@ -8,8 +8,10 @@ namespace Required\Traduttore\WebhookHandler; use Required\Traduttore\Project; +use Required\Traduttore\ProjectLocator; use Required\Traduttore\WebhookHandler; use WP_REST_Request; +use WP_REST_Response; /** * Base webhook handler class. @@ -85,4 +87,34 @@ protected function get_secret( ?Project $project = null ): ?string { */ return apply_filters( 'traduttore.webhook_secret', $secret, $this, $project ); } + + /** + * Return a valid project or errors. Allows to customize the pulled branch. + * + * @param string $repository Metadata to find a GlotPress project. + * @param string $default_branch Name of the repository's default branch. + * @param string $ref Name of the received branch through the webhook. + * @return \Required\Traduttore\Project|\WP_REST_Response|\WP_Error + */ + protected function resolve_project( string $repository, string $default_branch = '', string $ref = '' ): Project|WP_REST_Response|\WP_Error { + $locator = new ProjectLocator( $repository ); + $project = $locator->get_project(); + + if ( ! $project ) { + return new \WP_Error( '404', 'Could not find project for this repository', [ 'status' => 404 ] ); + } + + if ( empty( $default_branch ) || empty( $ref ) ) { + return $project; + } + + $branch = 'refs/heads/' . (string) apply_filters( 'traduttore.git_clone_branch', $default_branch, $project->get_repository_name() ); + + // We only care about the default or custom branch but don't want to send an error still. + if ( $branch !== $ref ) { + return new WP_REST_Response( [ 'result' => 'Not the default or custom branch' ] ); + } + + return $project; + } } diff --git a/inc/WebhookHandler/Bitbucket.php b/inc/WebhookHandler/Bitbucket.php index 45011ec..d45573b 100644 --- a/inc/WebhookHandler/Bitbucket.php +++ b/inc/WebhookHandler/Bitbucket.php @@ -7,6 +7,7 @@ namespace Required\Traduttore\WebhookHandler; +use Required\Traduttore\Project; use Required\Traduttore\ProjectLocator; use Required\Traduttore\Repository; use Required\Traduttore\Updater; @@ -79,12 +80,12 @@ public function callback(): \WP_Error|\WP_REST_Response { * @var array{repository: array{scm: string, full_name: string, links: array{html: array{href: string}}, is_private: bool}} $params */ $params = $this->request->get_params(); + $href = (string) $params['repository']['links']['html']['href']; - $locator = new ProjectLocator( $params['repository']['links']['html']['href'] ); - $project = $locator->get_project(); + $project = $this->resolve_project( $href ); - if ( ! $project ) { - return new \WP_Error( '404', 'Could not find project for this repository' ); + if ( ! $project instanceof Project ) { + return $project; } if ( ! $project->get_repository_vcs_type() ) { @@ -92,7 +93,7 @@ public function callback(): \WP_Error|\WP_REST_Response { } $project->set_repository_name( $params['repository']['full_name'] ); - $project->set_repository_url( $params['repository']['links']['html']['href'] ); + $project->set_repository_url( $href ); $ssh_url = sprintf( 'git@bitbucket.org:%s.git', $project->get_repository_name() ); $https_url = sprintf( 'https://bitbucket.org/%s.git', $project->get_repository_name() ); diff --git a/inc/WebhookHandler/GitHub.php b/inc/WebhookHandler/GitHub.php index 7df5dcc..bd7f1d6 100644 --- a/inc/WebhookHandler/GitHub.php +++ b/inc/WebhookHandler/GitHub.php @@ -7,6 +7,7 @@ namespace Required\Traduttore\WebhookHandler; +use Required\Traduttore\Project; use Required\Traduttore\ProjectLocator; use Required\Traduttore\Repository; use Required\Traduttore\Updater; @@ -117,24 +118,25 @@ public function callback(): \WP_Error|\WP_REST_Response { * @var array{repository: array{ default_branch?: string, html_url: string, full_name: string, ssh_url: string, clone_url: string, private: bool }, ref: string } $params */ - if ( ! isset( $params['repository']['default_branch'] ) ) { - return new \WP_Error( '400', 'Request incomplete', [ 'status' => 400 ] ); - } + $default_branch = (string) ( $params['repository']['default_branch'] ?? '' ); + $html_url = (string) $params['repository']['html_url']; + $ref = (string) $params['ref']; - // We only care about the default branch but don't want to send an error still. - if ( 'refs/heads/' . $params['repository']['default_branch'] !== $params['ref'] ) { - return new WP_REST_Response( [ 'result' => 'Not the default branch' ] ); + if ( '' === $default_branch + | '' === $html_url + | '' === $ref + ) { + return new \WP_Error( '400', 'Request incomplete', [ 'status' => 400 ] ); } - $locator = new ProjectLocator( $params['repository']['html_url'] ); - $project = $locator->get_project(); + $project = $this->resolve_project( $html_url, $default_branch, $ref ); - if ( ! $project ) { - return new \WP_Error( '404', 'Could not find project for this repository', [ 'status' => 404 ] ); + if ( ! $project instanceof Project ) { + return $project; } $project->set_repository_name( $params['repository']['full_name'] ); - $project->set_repository_url( $params['repository']['html_url'] ); + $project->set_repository_url( $html_url ); $project->set_repository_ssh_url( $params['repository']['ssh_url'] ); $project->set_repository_https_url( $params['repository']['clone_url'] ); $project->set_repository_visibility( false === $params['repository']['private'] ? 'public' : 'private' ); diff --git a/inc/WebhookHandler/GitLab.php b/inc/WebhookHandler/GitLab.php index 065d60c..1b12846 100644 --- a/inc/WebhookHandler/GitLab.php +++ b/inc/WebhookHandler/GitLab.php @@ -7,6 +7,7 @@ namespace Required\Traduttore\WebhookHandler; +use Required\Traduttore\Project; use Required\Traduttore\ProjectLocator; use Required\Traduttore\Repository; use Required\Traduttore\Updater; @@ -77,22 +78,26 @@ public function callback(): \WP_Error|\WP_REST_Response { * * @var array{project: array{default_branch: string, homepage: string, path_with_namespace: string, ssh_url: string, http_url: string, visibility_level: int}, ref: string} $params */ - $params = $this->request->get_params(); - - // We only care about the default branch but don't want to send an error still. - if ( 'refs/heads/' . $params['project']['default_branch'] !== $params['ref'] ) { - return new WP_REST_Response( [ 'result' => 'Not the default branch' ] ); + $params = $this->request->get_params(); + $default_branch = (string) $params['project']['default_branch']; + $homepage = (string) $params['project']['homepage']; + $ref = (string) $params['ref']; + + if ( '' === $default_branch + | '' === $homepage + | '' === $ref + ) { + return new \WP_Error( '400', 'Request incomplete', [ 'status' => 400 ] ); } - $locator = new ProjectLocator( $params['project']['homepage'] ); - $project = $locator->get_project(); + $project = $this->resolve_project( $homepage, $default_branch, $ref ); - if ( ! $project ) { - return new \WP_Error( '404', 'Could not find project for this repository' ); + if ( ! $project instanceof Project ) { + return $project; } $project->set_repository_name( $params['project']['path_with_namespace'] ); - $project->set_repository_url( $params['project']['homepage'] ); + $project->set_repository_url( $homepage ); $project->set_repository_ssh_url( $params['project']['ssh_url'] ); $project->set_repository_https_url( $params['project']['http_url'] ); $project->set_repository_visibility( 20 === $params['project']['visibility_level'] ? 'public' : 'private' ); diff --git a/tests/phpunit/tests/WebhookHandler/GitHub.php b/tests/phpunit/tests/WebhookHandler/GitHub.php index f7dc835..551864a 100644 --- a/tests/phpunit/tests/WebhookHandler/GitHub.php +++ b/tests/phpunit/tests/WebhookHandler/GitHub.php @@ -96,7 +96,7 @@ public function test_invalid_branch(): void { $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( [ 'result' => 'Not the default branch' ], $response->get_data() ); + $this->assertSame( [ 'result' => 'Not the default or custom branch' ], $response->get_data() ); } public function test_invalid_project(): void { diff --git a/tests/phpunit/tests/WebhookHandler/GitLab.php b/tests/phpunit/tests/WebhookHandler/GitLab.php index 19597df..8007b83 100644 --- a/tests/phpunit/tests/WebhookHandler/GitLab.php +++ b/tests/phpunit/tests/WebhookHandler/GitLab.php @@ -81,7 +81,7 @@ public function test_invalid_branch(): void { $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( [ 'result' => 'Not the default branch' ], $response->get_data() ); + $this->assertSame( [ 'result' => 'Not the default or custom branch' ], $response->get_data() ); } public function test_invalid_project(): void { @@ -106,6 +106,28 @@ public function test_invalid_project(): void { $this->assertErrorResponse( 404, $response ); } + public function test_request_incomplete(): void { + $request = new WP_REST_Request( 'POST', '/traduttore/v1/incoming-webhook' ); + $request->set_body_params( + [ + 'ref' => '', + 'project' => [ + 'default_branch' => 'master', + 'path_with_namespace' => 'wearerequired/traduttore', + 'homepage' => 'https://gitlab.com/wearerequired/traduttore', + 'http_url' => 'https://gitlab.com/wearerequired/traduttore.git', + 'ssh_url' => 'git@gitlab.com/wearerequired/traduttore.git', + 'visibility_level' => 20, + ], + ] + ); + $request->add_header( 'x-gitlab-event', 'Push Hook' ); + $request->add_header( 'x-gitlab-token', 'traduttore-test' ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 400, $response ); + } + public function test_valid_project(): void { $request = new WP_REST_Request( 'POST', '/traduttore/v1/incoming-webhook' ); $request->set_body_params( diff --git a/tests/phpunit/tests/WebhookHandler/LegacyGitHub.php b/tests/phpunit/tests/WebhookHandler/LegacyGitHub.php index f18d72d..922021a 100644 --- a/tests/phpunit/tests/WebhookHandler/LegacyGitHub.php +++ b/tests/phpunit/tests/WebhookHandler/LegacyGitHub.php @@ -93,7 +93,7 @@ public function test_invalid_branch(): void { $response = rest_get_server()->dispatch( $request ); $this->assertSame( 200, $response->get_status() ); - $this->assertSame( [ 'result' => 'Not the default branch' ], $response->get_data() ); + $this->assertSame( [ 'result' => 'Not the default or custom branch' ], $response->get_data() ); } public function test_invalid_project(): void {