From ef4bea7a786376e0b64e783bd7004e5ef1662665 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:21:25 +0000 Subject: [PATCH 1/2] Initial plan From 5b3d6bc784ed035a9bc36da410b248269a8e664e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:33:01 +0000 Subject: [PATCH 2/2] Make mr-iid optional: auto-select single MR or prompt interactively Co-authored-by: mglaman <3698644+mglaman@users.noreply.github.com> Agent-Logs-Url: https://github.com/mglaman/drupalorg-cli/sessions/dbee614d-7c46-457f-be68-7bc9979c9b16 --- .../Command/MergeRequest/MrCommandBase.php | 52 +++++++++++++++++-- .../MergeRequest/MrCommandBaseTest.php | 50 ++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/src/Command/MergeRequest/MrCommandBaseTest.php diff --git a/src/Cli/Command/MergeRequest/MrCommandBase.php b/src/Cli/Command/MergeRequest/MrCommandBase.php index 63ff407..69b3327 100644 --- a/src/Cli/Command/MergeRequest/MrCommandBase.php +++ b/src/Cli/Command/MergeRequest/MrCommandBase.php @@ -2,12 +2,17 @@ namespace mglaman\DrupalOrgCli\Command\MergeRequest; +use mglaman\DrupalOrg\Action\MergeRequest\ListMergeRequestsAction; +use mglaman\DrupalOrg\Enum\MergeRequestState; +use mglaman\DrupalOrg\GitLab\Client as GitLabClient; use mglaman\DrupalOrg\GitLab\MergeRequestRef; +use mglaman\DrupalOrg\Result\MergeRequest\MergeRequestItem; use mglaman\DrupalOrgCli\Command\Command; use mglaman\DrupalOrgCli\Command\Issue\IssueCommandBase; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; abstract class MrCommandBase extends IssueCommandBase { @@ -50,9 +55,50 @@ protected function initialize(InputInterface $input, OutputInterface $output): v parent::initialize($input, $output); $mrIid = $this->stdIn->getArgument('mr-iid'); - if ($mrIid === null || $mrIid === '') { - throw new \RuntimeException('Argument mr-iid is required.'); + if ($mrIid !== null && $mrIid !== '') { + $this->mrIid = (int) $mrIid; + return; + } + + // mr-iid not provided — auto-select from open merge requests. + $listAction = new ListMergeRequestsAction($this->client, new GitLabClient()); + $listResult = $listAction($this->nid, MergeRequestState::Opened); + $mergeRequests = $listResult->mergeRequests; + + if ($mergeRequests === []) { + throw new \RuntimeException('No open merge requests found for this issue.'); + } + + if (count($mergeRequests) === 1) { + $this->mrIid = $mergeRequests[0]->iid; + $this->stdOut->writeln(sprintf( + 'Auto-selected MR !%d: %s', + $mergeRequests[0]->iid, + $mergeRequests[0]->title + )); + return; + } + + // Multiple open MRs. + if (!self::$interactive) { + $this->stdErr->writeln('Multiple open merge requests found. Specify one of:'); + foreach ($mergeRequests as $mr) { + $this->stdErr->writeln(sprintf(' !%d — %s', $mr->iid, $mr->title)); + } + throw new \RuntimeException('Argument mr-iid is required when multiple merge requests are open.'); + } + + $choices = array_map( + static fn(MergeRequestItem $mr) => sprintf('!%d — %s', $mr->iid, $mr->title), + $mergeRequests + ); + /** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = new ChoiceQuestion('Select a merge request:', $choices); + $selected = (string) $helper->ask($input, $output, $question); + if (!preg_match('/^!(\d+)/', $selected, $matches)) { + throw new \RuntimeException('Failed to extract merge request IID from selection.'); } - $this->mrIid = (int) $mrIid; + $this->mrIid = (int) $matches[1]; } } diff --git a/tests/src/Command/MergeRequest/MrCommandBaseTest.php b/tests/src/Command/MergeRequest/MrCommandBaseTest.php new file mode 100644 index 0000000..90033c7 --- /dev/null +++ b/tests/src/Command/MergeRequest/MrCommandBaseTest.php @@ -0,0 +1,50 @@ +configureNidAndMrIid(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + return 0; + } + }; + self::assertInstanceOf(MrCommandBase::class, $command); + } + + public function testConfigureNidAndMrIidAddsArguments(): void + { + $command = new class ('test:mr-command') extends MrCommandBase { + protected function configure(): void + { + $this->configureNidAndMrIid(); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + return 0; + } + }; + + $definition = $command->getDefinition(); + self::assertTrue($definition->hasArgument('nid')); + self::assertTrue($definition->hasArgument('mr-iid')); + self::assertFalse($definition->getArgument('nid')->isRequired()); + self::assertFalse($definition->getArgument('mr-iid')->isRequired()); + } +}