Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable running in subdirectories #76

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions app/Commands/Audit.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public function handle(): int
['getGlobalComposerBinDir', Platform::getGlobalComposerBinDir()],
['isWindows', $platform->isWindows() ? 'yes' : 'no'],
['isNotWindows', $platform->isNotWindows() ? 'yes' : 'no'],
['gitIsInitialized', $platform->gitIsInitialized() ? 'yes' : 'no'],
['gitIsNotInitialized', $platform->gitIsNotInitialized() ? 'yes' : 'no'],
['gitIsInitialized', Platform::gitIsInitialized() ? 'yes' : 'no'],
['gitIsNotInitialized', Platform::gitIsNotInitialized() ? 'yes' : 'no'],
['- global -', ''],
['base_path', base_path()],
['normalized base_path', Platform::normalizePath(base_path())],
Expand Down
5 changes: 2 additions & 3 deletions app/Commands/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function __construct(

public function handle(): int
{
if ($this->platform->gitIsNotInitialized()) {
if (Platform::gitIsNotInitialized()) {
$this->error('Git has not been initialized in this project, aborting...');

return Command::FAILURE;
Expand Down Expand Up @@ -73,8 +73,7 @@ public function handle(): int
if ($this->option('verbose')) {
$this->info('Verifying hooks are executable...');
}
exec('chmod +x '.Platform::cwd('.git/hooks').'/*');
exec('chmod +x '.Whisky::base_path('bin/run-hook'));
exec('chmod +x '.Platform::git_path('hooks').'/*');
}

$this->info('Git hooks installed successfully.');
Expand Down
4 changes: 2 additions & 2 deletions app/Commands/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public function handle(): int
return Command::FAILURE;
}

if (File::exists(Platform::cwd('.git/hooks/skip-once'))) {
File::delete(Platform::cwd('.git/hooks/skip-once'));
if (File::exists(Platform::git_path('hooks/skip-once'))) {
File::delete(Platform::git_path('hooks/skip-once'));

return Command::SUCCESS;
}
Expand Down
2 changes: 1 addition & 1 deletion app/Commands/SkipOnce.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class SkipOnce extends Command

public function handle(): int
{
File::put(Platform::cwd('.git/hooks/skip-once'), '');
File::put(Platform::git_path('hooks/skip-once'), '');

$this->info('Next hook will be skipped.');
$this->line('If the action you\'re about to take has a `pre` and `post` hook');
Expand Down
17 changes: 10 additions & 7 deletions app/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function __construct(

public function uninstall(): bool
{
$path = Platform::cwd(".git/hooks/{$this->hook}");
$path = Platform::git_path("hooks/{$this->hook}");

if ($this->fileIsMissing()) {
// This should be unreachable.
Expand All @@ -53,7 +53,7 @@ public function uninstall(): bool
// use ensureFileExists instead?
public function fileExists(): bool
{
return File::exists(Platform::cwd(".git/hooks/{$this->hook}"));
return File::exists(Platform::git_path("hooks/{$this->hook}"));
}

public function fileIsMissing(): bool
Expand Down Expand Up @@ -82,7 +82,7 @@ public function isNotEnabled(): bool
*/
public function enable(): void
{
File::put(Platform::cwd(".git/hooks/{$this->hook}"), '#!/bin/sh'.PHP_EOL);
File::put(Platform::git_path("hooks/{$this->hook}"), '#!/bin/sh'.PHP_EOL);
}

/**
Expand All @@ -101,7 +101,7 @@ public function ensureExecutable(): void
public function isInstalled(): bool
{
return Str::contains(
File::get(Platform::cwd(".git/hooks/{$this->hook}")),
File::get(Platform::git_path("hooks/{$this->hook}")),
$this->getSnippets()->toArray(),
);
}
Expand All @@ -112,7 +112,7 @@ public function isInstalled(): bool
public function install(): void
{
File::append(
Platform::cwd(".git/hooks/{$this->hook}"),
Platform::git_path("hooks/{$this->hook}"),
$this->getSnippets()->first().PHP_EOL,
);
}
Expand All @@ -139,9 +139,12 @@ public function getScripts(): Collection
*/
public function getSnippets(): Collection
{
$cwd = Platform::cwd();

return collect([
"{$this->bin} run {$this->hook} \"$1\"",
"pushd {$cwd} && {$this->bin} run {$this->hook} \"$1\" && popd",
// Legacy Snippets.
"{$this->bin} run {$this->hook} \"$1\"",
"{$this->bin} run {$this->hook}",
"eval \"$({$this->bin} get-run-cmd {$this->hook})\"",
"eval \"$(./vendor/bin/whisky get-run-cmd {$this->hook})\"",
Expand Down Expand Up @@ -172,7 +175,7 @@ public static function all(int $flags = self::FROM_CONFIG): Collection
$result = collect();

if ($flags & self::FROM_GIT) {
$result->push(...collect(File::files(Platform::cwd('.git/hooks')))
$result->push(...collect(File::files(Platform::git_path('hooks')))
->map(fn (SplFileInfo $file) => $file->getFilename())
->filter(fn (string $filename) => ! str_ends_with($filename, 'sample'))
);
Expand Down
31 changes: 26 additions & 5 deletions app/Platform.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

namespace ProjektGopher\Whisky;

use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;

class Platform
{
// Possibly use `git rev-parse --absolute-git-dir` instead for consistency.
const GIT_DIR_CMD = 'git rev-parse --git-dir';

public static function cwd(string $path = ''): string
{
if ($path) {
Expand Down Expand Up @@ -33,6 +36,24 @@ public static function normalizePath(string $path): string
return $path;
}

public static function git_path(string $path = ''): ?string
{
/**
* We use the `Process` facade here to run this
* command instead of `shell_exec()` because
* it's easier to mock in our test suite.
*/
$output = Process::run(static::GIT_DIR_CMD);

if ($output->failed()) {
return null;
}

return empty($path) === true
? static::normalizePath(rtrim($output->output(), "\n"))
: static::normalizePath(rtrim($output->output(), "\n")."/{$path}");
}

public static function getGlobalComposerHome(): string
{
return rtrim(shell_exec('composer -n global config home --quiet'), "\n");
Expand All @@ -58,13 +79,13 @@ public function isNotWindows(): bool
return ! $this->isWindows();
}

public function gitIsInitialized(): bool
public static function gitIsInitialized(): bool
{
return File::exists(Platform::cwd('.git'));
return static::git_path() !== null;
}

public function gitIsNotInitialized(): bool
public static function gitIsNotInitialized(): bool
{
return ! $this->gitIsInitialized();
return ! static::gitIsInitialized();
}
}
15 changes: 11 additions & 4 deletions tests/Feature/InstallTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php

use Illuminate\Support\Facades\File;
use Illuminate\Process\FakeProcessResult;
use Illuminate\Support\Facades\Process;
use ProjektGopher\Whisky\Platform;

it('has an install command', function () {
$this->artisan('list')
Expand All @@ -9,10 +11,15 @@
});

it('fails if git is not initialized', function () {
File::shouldReceive('exists')
Process::shouldReceive('run')
->once()
->with(normalizePath(base_path('.git')))
->andReturnFalse();
->with(Platform::GIT_DIR_CMD)
->andReturn(new FakeProcessResult(
command: Platform::GIT_DIR_CMD,
exitCode: 1,
output: '',
errorOutput: 'fatal: not a git repository (or any of the parent directories): .git\n',
));

$this->artisan('install')
->expectsOutputToContain('Git has not been initialized in this project, aborting...')
Expand Down
10 changes: 5 additions & 5 deletions tests/Feature/RunTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

File::shouldReceive('exists')
->once()
->with(Platform::cwd('.git/hooks/skip-once'))
->with(Platform::git_path('hooks/skip-once'))
->andReturnTrue();

File::shouldReceive('delete')
->once()
->with(Platform::cwd('.git/hooks/skip-once'))
->with(Platform::git_path('hooks/skip-once'))
->andReturnTrue();

$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
Expand Down Expand Up @@ -47,7 +47,7 @@
->andReturnFalse();

File::shouldReceive('exists')
->with(Platform::cwd('.git/hooks/skip-once'))
->with(Platform::git_path('hooks/skip-once'))
->andReturnFalse();

$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
Expand Down Expand Up @@ -85,7 +85,7 @@
->andReturnFalse();

File::shouldReceive('exists')
->with(Platform::cwd('.git/hooks/skip-once'))
->with(Platform::git_path('hooks/skip-once'))
->andReturnFalse();

$tmp_file = Platform::temp_test_path('whisky_test_pre_commit');
Expand Down Expand Up @@ -115,7 +115,7 @@
->andReturnFalse();

File::shouldReceive('exists')
->with(Platform::cwd('.git/hooks/skip-once'))
->with(Platform::git_path('hooks/skip-once'))
->andReturnFalse();

$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/SkipOnceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
->expectsOutputToContain('Next hook will be skipped.')
->assertExitCode(0);

expect(File::exists(Platform::cwd('.git/hooks/skip-once')))->toBeTrue();
expect(File::exists(Platform::git_path('hooks/skip-once')))->toBeTrue();

// Cleanup
File::delete(Platform::cwd('.git/hooks/skip-once'));
File::delete(Platform::git_path('hooks/skip-once'));
});