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); + } +}