Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/Api/ProjectRemote.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace mglaman\DrupalOrg;

/**
* The Drupal.org project a git remote URL points at.
*/
final class ProjectRemote
{
/**
* Matches SSH (git@host:path), ssh://, and https:// remotes on either
* Drupal.org git host, with or without a trailing ".git".
*/
private const PATTERN = '#^(?:(?:ssh|https?)://)?(?:[^@/\s]+@)?git\.(?:drupal|drupalcode)\.org[:/]project/(?<name>[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']);
}
}
27 changes: 13 additions & 14 deletions src/Cli/Command/Project/ProjectCommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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());
}
}
36 changes: 36 additions & 0 deletions tests/src/ProjectRemoteTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace mglaman\DrupalOrg\Tests;

use mglaman\DrupalOrg\ProjectRemote;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

#[CoversClass(ProjectRemote::class)]
class ProjectRemoteTest extends TestCase
{
/**
* @return array<string, array{0: string, 1: ?string}>
*/
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);
}
}