From 3e5a583d277d4d5131eefdbd2bb36c8fd8993fa6 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Fri, 28 Aug 2026 11:21:20 -0500 Subject: [PATCH] fix: derive project machine name from git remote when argument is omitted ProjectCommandBase checked InputInterface::hasArgument('project'), which only reports whether the argument is defined on the command, not whether the user passed it. It was always true, so the git remote fallback never ran and the null argument became an empty project name. The parsing now lives in ProjectRemote so it can be unit tested against SSH, HTTPS, and .git-less remote forms, and a missing derivation reports a clear error. Closes #346 Co-Authored-By: Claude Fable 5 --- src/Api/ProjectRemote.php | 30 ++++++++++++++++ .../Command/Project/ProjectCommandBase.php | 27 +++++++------- tests/src/ProjectRemoteTest.php | 36 +++++++++++++++++++ 3 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 src/Api/ProjectRemote.php create mode 100644 tests/src/ProjectRemoteTest.php diff --git a/src/Api/ProjectRemote.php b/src/Api/ProjectRemote.php new file mode 100644 index 0000000..4db9ed0 --- /dev/null +++ b/src/Api/ProjectRemote.php @@ -0,0 +1,30 @@ +[A-Za-z0-9_-]+)(?:\.git)?/?$#'; + + public function __construct(public readonly string $machineName) + { + } + + public static function tryParse(string $remoteUrl): ?self + { + $matches = []; + if (preg_match(self::PATTERN, trim($remoteUrl), $matches) !== 1) { + return null; + } + return new self($matches['name']); + } +} diff --git a/src/Cli/Command/Project/ProjectCommandBase.php b/src/Cli/Command/Project/ProjectCommandBase.php index 6ac8569..4211595 100644 --- a/src/Cli/Command/Project/ProjectCommandBase.php +++ b/src/Cli/Command/Project/ProjectCommandBase.php @@ -3,6 +3,7 @@ namespace mglaman\DrupalOrgCli\Command\Project; use mglaman\DrupalOrg\Entity\Project; +use mglaman\DrupalOrg\ProjectRemote; use mglaman\DrupalOrgCli\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -29,17 +30,17 @@ protected function initialize(InputInterface $input, OutputInterface $output): v { parent::initialize($input, $output); - if (!$this->stdIn->hasArgument('project')) { + $projectName = $this->stdIn->getArgument('project'); + if (!is_string($projectName) || $projectName === '') { $this->debug("Argument project not provided. Trying to get it from the remote URL of the current repository."); - $this->projectName = $this->getProjectFromRemote(); - if ($this->projectName === '') { - $this->stdErr->writeln("Failed to find project / machine name from current Git repository."); + $remote = ProjectRemote::tryParse($this->getRemoteUrl()); + if ($remote === null) { + $this->stdErr->writeln("Could not determine the project from the git remote; pass the machine name as an argument."); exit(1); } - } else { - $projectName = $this->stdIn->getArgument('project') ?? ''; - $this->projectName = $projectName; + $projectName = $remote->machineName; } + $this->projectName = $projectName; // The kanban and link command doesn't need the project data from drupal.org, // but checking that the project exists makes sense for all project commands. @@ -53,17 +54,15 @@ protected function initialize(InputInterface $input, OutputInterface $output): v } /** - * Gets project from remote origin name. + * Gets the origin remote URL of the current repository. * * @return string - * The project name. + * The remote URL, or an empty string when there is no origin remote. */ - protected function getProjectFromRemote(): string + protected function getRemoteUrl(): string { - $process = new Process((array) 'git config --get remote.origin.url'); + $process = new Process(['git', 'config', '--get', 'remote.origin.url']); $process->run(); - $remote_url = trim($process->getOutput()); - preg_match('#.*\/(.*)\.git$#', $remote_url, $matches); - return $matches[1] ?? ''; + return trim($process->getOutput()); } } diff --git a/tests/src/ProjectRemoteTest.php b/tests/src/ProjectRemoteTest.php new file mode 100644 index 0000000..fd97ed2 --- /dev/null +++ b/tests/src/ProjectRemoteTest.php @@ -0,0 +1,36 @@ + + */ + public static function remoteUrlProvider(): array + { + return [ + 'ssh scp-style' => ['git@git.drupal.org:project/json_form_widget.git', 'json_form_widget'], + 'ssh scp-style on drupalcode' => ['git@git.drupalcode.org:project/json_form_widget.git', 'json_form_widget'], + 'https' => ['https://git.drupalcode.org/project/json_form_widget.git', 'json_form_widget'], + 'https without .git' => ['https://git.drupalcode.org/project/json_form_widget', 'json_form_widget'], + 'ssh scheme' => ['ssh://git@git.drupal.org/project/json_form_widget.git', 'json_form_widget'], + 'trailing newline from git output' => ["git@git.drupal.org:project/json_form_widget.git\n", 'json_form_widget'], + 'github remote' => ['git@github.com:mglaman/drupalorg-cli.git', null], + 'issue fork' => ['git@git.drupal.org:issue/json_form_widget-3000000.git', null], + 'empty' => ['', null], + ]; + } + + #[DataProvider('remoteUrlProvider')] + public function testTryParse(string $remoteUrl, ?string $expected): void + { + self::assertSame($expected, ProjectRemote::tryParse($remoteUrl)?->machineName); + } +}