Skip to content

Commit

Permalink
Merge pull request #5 from bearsunday/git
Browse files Browse the repository at this point in the history
Add FormulaException handling to improve robustness
  • Loading branch information
koriym authored Nov 14, 2024
2 parents b077f01 + 4d91be2 commit 26a178e
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 39 deletions.
18 changes: 11 additions & 7 deletions bin/bear-cli-gen
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare(strict_types=1);

use BEAR\AppMeta\Meta;
use BEAR\Cli\CompileScript;
use BEAR\Cli\Exception\FormulaException;
use BEAR\Cli\GenFormula;
use BEAR\Cli\GenScript;
use BEAR\Cli\GitCommand;
Expand All @@ -30,15 +31,18 @@ try {
}

// Handle formula generation
if ($result['formula']) {
$formulaDir = dirname($result['formula']['path']);
if (! is_dir($formulaDir)) {
mkdir($formulaDir, 0755, true);
}
file_put_contents($result['formula']['path'], $result['formula']['content']);
echo sprintf('Homebrew formula has been generated: %s', $result['formula']['path']) . PHP_EOL;
if ($result['formula'] instanceof FormulaException) {
echo sprintf('Homebrew formula generation skipped: %s', $result['formula']->getMessage()) . PHP_EOL;
exit(0);
}

$formulaDir = dirname($result['formula']['path']);
if (! is_dir($formulaDir)) {
mkdir($formulaDir, 0755, true);
}
file_put_contents($result['formula']['path'], $result['formula']['content']);
echo sprintf('Homebrew formula has been generated: %s', $result['formula']['path']) . PHP_EOL;

exit(0);
} catch (\Exception $e) {
echo 'Error: ' . $e->getMessage() . PHP_EOL;
Expand Down
9 changes: 6 additions & 3 deletions src/CompileScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use BEAR\AppMeta\Meta;
use BEAR\Cli\Attribute\Cli;
use BEAR\Cli\Exception\FormulaException;
use ReflectionClass;
use ReflectionMethod;

Expand All @@ -15,6 +16,7 @@
use function mkdir;
use function sprintf;

/** @psalm-import-type Formula from GenFormula */
final class CompileScript
{
public function __construct(
Expand All @@ -28,7 +30,7 @@ public function __construct(
*
* @return array{
* sources: array<CommandSource>,
* formula: array{path: string, content: string}|null
* formula: FormulaException|Formula
* }
*/
public function compile(Meta $meta): array
Expand All @@ -54,9 +56,10 @@ public function compile(Meta $meta): array
}

// Generate formula if it's a git repository
$formula = null;
if (is_dir($meta->appDir . '/.git')) {
try {
$formula = ($this->genFormula)($meta);
} catch (FormulaException $e) {
$formula = $e;
}

$this->dumpSources($sources, $meta->appDir . '/bin/cli');
Expand Down
9 changes: 9 additions & 0 deletions src/Exception/FormulaException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace BEAR\Cli\Exception;

final class FormulaException extends RuntimeException
{
}
9 changes: 9 additions & 0 deletions src/Exception/GitExecException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace BEAR\Cli\Exception;

class GitExecException extends RuntimeException
{
}
45 changes: 32 additions & 13 deletions src/GenFormula.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
namespace BEAR\Cli;

use BEAR\AppMeta\Meta;
use BEAR\Cli\Exception\RuntimeException;
use BEAR\Cli\Exception\FormulaException;
use Throwable;

use function is_dir;
use function preg_match;
use function sprintf;
use function str_replace;
Expand All @@ -17,8 +19,10 @@
use const PHP_MINOR_VERSION;

/**
* @psalm-type RepoInfo = array{org: string, repo: string}
* @psalm-type Formula = array{path: string, content: string}
* Generate Homebrew formula for the application
*
* @psalm-type Formula=array{path: string, content: string}
* @psalm-type RepoInfo=array{org: string, repo: string}
*/
final class GenFormula
{
Expand Down Expand Up @@ -46,7 +50,8 @@ class %s < Formula
# Generate CLI commands and get the generated command name
output = Utils.safe_popen_read("#{libexec}/vendor/bear/cli/bin/bear-cli-gen", "%s")
# Extract multiple commands from the output
generated_commands = output.scan(/CLI commands have been generated.*?:\n\s+(.+)$/m)[0][0].split(/\s+/)
generated_commands = output.scan(/CLI commands have been generated.*?:
\s+(.+)$/m)[0][0].split(/\s+/)
ohai "Generated commands:", generated_commands.join(", ")
generated_commands.each do |command|
Expand Down Expand Up @@ -83,7 +88,9 @@ public function __construct(
private function extractRepoInfo(string $url): array
{
if (! preg_match(self::GITHUB_REPOSITORY_PATTERN, $url, $matches)) {
throw new RuntimeException('Invalid GitHub URL format');
throw new FormulaException(
'Invalid GitHub URL format. URL must be in format: https://github.com/owner/repo or [email protected]:owner/repo',
);
}

return [
Expand All @@ -102,28 +109,40 @@ private function generateFormulaName(string $repoName): string
/** @return Formula */
public function __invoke(Meta $meta): array
{
$repoUrl = $this->gitCommand->getRemoteUrl();
if (! is_dir($meta->appDir . '/.git')) {
throw new FormulaException('Not a git repository. Initialize a git repository first with: git init'); // @codeCoverageIgnore
}

try {
$remoteUrl = $this->gitCommand->getRemoteUrl();
if (empty($remoteUrl)) {
throw new FormulaException('Git remote URL is not configured. Set a remote with: git remote add origin <url>');
}
} catch (Throwable $e) {
throw new FormulaException('Failed to get Git remote URL: ' . $e->getMessage());
}

$repoInfo = $this->extractRepoInfo($repoUrl);
$branch = $this->gitCommand->detectMainBranch($repoUrl);
$repoUrl = $this->gitCommand->getRemoteUrl();
$repoInfo = $this->extractRepoInfo($remoteUrl);

try {
$branch = $this->gitCommand->detectMainBranch($remoteUrl);
} catch (Throwable $e) {
throw new FormulaException('Failed to detect main branch: ' . $e->getMessage());
}

// Generate formula/tap name
$formulaName = $this->generateFormulaName($repoInfo['repo']);

// Generate formula content
$content = sprintf(
self::TEMPLATE,
ucfirst($formulaName),
$meta->name,
"https://github.com/{$repoInfo['org']}/{$repoInfo['repo']}",
$repoUrl,
$remoteUrl,
$branch,
PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
str_replace('\\', '\\\\', $meta->name),
);

// Define formula path
$path = sprintf(
self::HOMEBREW_FORMULA_PATH,
$meta->appDir,
Expand Down
2 changes: 1 addition & 1 deletion tests/CompileScriptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function testCompile(): void
$sources = $compileResult['sources'];
$formula = $compileResult['formula'];
$this->assertArrayHasKey('formula', $compileResult);
$this->assertNotEmpty($formula);
$this->assertIsArray($formula);
$content = $formula['content'];
$this->assertStringContainsString('class Bearcli < Formula', $content);
$this->assertStringContainsString('desc', $content);
Expand Down
87 changes: 72 additions & 15 deletions tests/GenFormulaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace BEAR\Cli;

use BEAR\AppMeta\Meta;
use BEAR\Cli\Exception\RuntimeException;
use BEAR\Cli\Exception\FormulaException;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

Expand All @@ -27,7 +27,6 @@ protected function setUp(): void
mkdir($this->tmpDir);
mkdir($this->tmpDir . '/.git');

// Setup Meta
$this->meta = new Meta('FakeVendor\FakeProject');
$ref = new ReflectionClass($this->meta);
$prop = $ref->getProperty('appDir');
Expand All @@ -40,8 +39,7 @@ protected function tearDown(): void
rmdir($this->tmpDir);
}

/** @test */
public function generatesFormulaSuccessfully(): void
public function testGeneratesFormulaSuccessfully(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): string
Expand All @@ -54,6 +52,7 @@ public function detectMainBranch(string $repoUrl): string
return 'main';
}
};

$genFormula = new GenFormula($gitCommand);
$result = $genFormula($this->meta);

Expand All @@ -68,16 +67,78 @@ public function detectMainBranch(string $repoUrl): string
// Check formula content
$content = $result['content'];
$this->assertStringContainsString('class Fakeproject < Formula', $content);
$this->assertStringContainsString('desc "Command line interface for FakeVendor\FakeProject application"', $content);
$this->assertStringContainsString('desc "Command line interface for FakeVendor\\FakeProject application"', $content);
$this->assertStringContainsString('homepage "https://github.com/fakevendor/fake-project"', $content);
$this->assertStringContainsString('head "https://github.com/fakevendor/fake-project.git"', $content);
$this->assertStringContainsString('depends_on "php@', $content);
$this->assertStringContainsString('depends_on "composer"', $content);
$this->assertStringContainsString('bear-cli-gen", "FakeVendor\\\\FakeProject', $content);
}

/** @test */
public function throwsExceptionForInvalidGithubUrl(): void
public function testThrowsExceptionWhenRemoteUrlIsEmpty(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): string
{
return '';
}

public function detectMainBranch(string $repoUrl): string
{
return 'main';
}
};
$genFormula = new GenFormula($gitCommand);

$this->expectException(FormulaException::class);
$this->expectExceptionMessage('Git remote URL is not configured');

$genFormula($this->meta);
}

public function testThrowsExceptionWhenGetRemoteUrlFails(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): never
{
throw new FormulaException('Command failed');
}

public function detectMainBranch(string $repoUrl): string
{
return 'main';
}
};
$genFormula = new GenFormula($gitCommand);

$this->expectException(FormulaException::class);
$this->expectExceptionMessage('Failed to get Git remote URL: Command failed');

$genFormula($this->meta);
}

public function testThrowsExceptionWhenDetectMainBranchFails(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): string
{
return 'https://github.com/fakevendor/fake-project.git';
}

public function detectMainBranch(string $repoUrl): never
{
throw new FormulaException('Branch detection failed');
}
};
$genFormula = new GenFormula($gitCommand);

$this->expectException(FormulaException::class);
$this->expectExceptionMessage('Failed to detect main branch: Branch detection failed');

$genFormula($this->meta);
}

public function testThrowsExceptionForInvalidGithubUrl(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): string
Expand All @@ -92,17 +153,14 @@ public function detectMainBranch(string $repoUrl): string
};
$genFormula = new GenFormula($gitCommand);

$this->expectException(RuntimeException::class);
$this->expectException(FormulaException::class);
$this->expectExceptionMessage('Invalid GitHub URL format');

$genFormula($this->meta);
}

/**
* @test
* @dataProvider provideRepositoryNames
*/
public function generatesCorrectFormulaNameForVariousRepositories(
/** @dataProvider provideRepositoryNames */
public function testGeneratesCorrectFormulaNameForVariousRepositories(
string $repoName,
string $expectedFormulaName,
): void {
Expand Down Expand Up @@ -144,8 +202,7 @@ public static function provideRepositoryNames(): array
];
}

/** @test */
public function handlesSSHUrlConversion(): void
public function testHandlesSSHUrlConversion(): void
{
$gitCommand = new class implements GitCommandInterface {
public function getRemoteUrl(): string
Expand Down

0 comments on commit 26a178e

Please sign in to comment.