From a39a3a50bfb045ff7bd23b2c25437b87f51f8626 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 6 May 2026 16:37:35 +0100 Subject: [PATCH 1/2] Add merge request merge train API --- CHANGELOG.md | 1 + src/Api/MergeRequests.php | 5 +++++ tests/Api/MergeRequestsTest.php | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b288d4ab..7eabdc8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Add support for merge request resource label event endpoints * Add support for merge request and merge request note award emoji endpoints * Add support for `MergeRequests::remove` +* Add support for `MergeRequests::addToMergeTrain` * Add support for container registry endpoints * Add support for `Environments::stopStale` * Add support for `last_activity_after` and `last_activity_before` in `Groups::projects` diff --git a/src/Api/MergeRequests.php b/src/Api/MergeRequests.php index 4f3fa5ea..5f931bb5 100644 --- a/src/Api/MergeRequests.php +++ b/src/Api/MergeRequests.php @@ -318,6 +318,11 @@ public function merge(int|string $project_id, int $mr_iid, array $parameters = [ return $this->put($this->getProjectPath($project_id, 'merge_requests/'.self::encodePath($mr_iid).'/merge'), $parameters); } + public function addToMergeTrain(int|string $project_id, int $mr_iid, array $parameters = []): mixed + { + return $this->post($this->getProjectPath($project_id, 'merge_trains/merge_requests/'.self::encodePath($mr_iid)), $parameters); + } + public function showNotes(int|string $project_id, int $mr_iid): mixed { return $this->get($this->getProjectPath($project_id, 'merge_requests/'.self::encodePath($mr_iid).'/notes')); diff --git a/tests/Api/MergeRequestsTest.php b/tests/Api/MergeRequestsTest.php index 0d664c8b..9dff846e 100644 --- a/tests/Api/MergeRequestsTest.php +++ b/tests/Api/MergeRequestsTest.php @@ -347,6 +347,29 @@ public function shouldMergeMergeRequest(): void $this->assertEquals($expectedArray, $api->merge(1, 2, ['merge_commit_message' => 'Accepted'])); } + #[Test] + public function shouldAddMergeRequestToMergeTrain(): void + { + $expectedArray = ['id' => 267, 'target_branch' => 'main', 'status' => 'idle']; + + $api = $this->getApiMock(); + $api->expects($this->once()) + ->method('post') + ->with('projects/1/merge_trains/merge_requests/2', [ + 'auto_merge' => true, + 'sha' => 'abc123', + 'squash' => true, + ]) + ->willReturn($expectedArray) + ; + + $this->assertEquals($expectedArray, $api->addToMergeTrain(1, 2, [ + 'auto_merge' => true, + 'sha' => 'abc123', + 'squash' => true, + ])); + } + #[Test] public function shouldGetNotes(): void { From 90348b3d0802c52bb75bbc2cf0e08aa1cf3093b9 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 6 May 2026 16:57:37 +0100 Subject: [PATCH 2/2] Restrict merge train parameters --- src/Api/MergeRequests.php | 26 +++++++++++++++++++++++++- tests/Api/MergeRequestsTest.php | 4 +++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Api/MergeRequests.php b/src/Api/MergeRequests.php index 5f931bb5..bb557e51 100644 --- a/src/Api/MergeRequests.php +++ b/src/Api/MergeRequests.php @@ -17,6 +17,7 @@ use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; class MergeRequests extends AbstractApi { @@ -318,9 +319,32 @@ public function merge(int|string $project_id, int $mr_iid, array $parameters = [ return $this->put($this->getProjectPath($project_id, 'merge_requests/'.self::encodePath($mr_iid).'/merge'), $parameters); } + /** + * @param array $parameters { + * + * @var bool $auto_merge add the merge request to the merge train when checks pass + * @var string $sha must match the HEAD of the source branch if present + * @var bool $squash squash commits into a single commit on merge + * @var bool $when_pipeline_succeeds deprecated in GitLab 17.11. Use auto_merge instead. + * } + */ public function addToMergeTrain(int|string $project_id, int $mr_iid, array $parameters = []): mixed { - return $this->post($this->getProjectPath($project_id, 'merge_trains/merge_requests/'.self::encodePath($mr_iid)), $parameters); + $resolver = new OptionsResolver(); + $resolver->setDefined('auto_merge') + ->setAllowedTypes('auto_merge', 'bool') + ; + $resolver->setDefined('sha') + ->setAllowedTypes('sha', 'string') + ; + $resolver->setDefined('squash') + ->setAllowedTypes('squash', 'bool') + ; + $resolver->setDefined('when_pipeline_succeeds') + ->setAllowedTypes('when_pipeline_succeeds', 'bool') + ; + + return $this->post($this->getProjectPath($project_id, 'merge_trains/merge_requests/'.self::encodePath($mr_iid)), $resolver->resolve($parameters)); } public function showNotes(int|string $project_id, int $mr_iid): mixed diff --git a/tests/Api/MergeRequestsTest.php b/tests/Api/MergeRequestsTest.php index 9dff846e..57654e65 100644 --- a/tests/Api/MergeRequestsTest.php +++ b/tests/Api/MergeRequestsTest.php @@ -350,7 +350,7 @@ public function shouldMergeMergeRequest(): void #[Test] public function shouldAddMergeRequestToMergeTrain(): void { - $expectedArray = ['id' => 267, 'target_branch' => 'main', 'status' => 'idle']; + $expectedArray = [['id' => 267, 'target_branch' => 'main', 'status' => 'idle']]; $api = $this->getApiMock(); $api->expects($this->once()) @@ -359,6 +359,7 @@ public function shouldAddMergeRequestToMergeTrain(): void 'auto_merge' => true, 'sha' => 'abc123', 'squash' => true, + 'when_pipeline_succeeds' => false, ]) ->willReturn($expectedArray) ; @@ -367,6 +368,7 @@ public function shouldAddMergeRequestToMergeTrain(): void 'auto_merge' => true, 'sha' => 'abc123', 'squash' => true, + 'when_pipeline_succeeds' => false, ])); }