Skip to content

Commit 9d012d7

Browse files
[ Feat ] Pass optional arguments to hooks (#77)
* Adds optional argument param to run command * update snippet to pass optional argument * add commit-msg hook to project for local testing * Update callouts in README * update README * remove commit-msg hook used for local testing * fill in tests, fix skipped test * add test temp path helper for windows garbage * add temporary folder in project that persists * fix tests for windows... maybe? * WINDOWS! FML * make test more strict * trim extra dbl_quotes from windows cmd line * accomodate different cli behavior for passing string arguments on windows * try again * blah * fuck you * reduce expectations and try again
1 parent 98fe07a commit 9d012d7

File tree

6 files changed

+173
-9
lines changed

6 files changed

+173
-9
lines changed

README.md

+32-4
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,35 @@ The `install` command will create a `whisky.json` file in your project root:
5858

5959
For a complete list of supported git hooks, see the [Git Documentation](https://git-scm.com/docs/githooks#_hooks).
6060

61-
> **Warning** all hooks are **evaluated as-is** in the terminal. Keep this in mind when committing anything involving changes to your `whisky.json`.
61+
> [!CAUTION]
62+
> all hooks are **evaluated as-is** in the terminal. Keep this in mind when committing anything involving changes to your `whisky.json`.
6263
6364
Adding or removing any **hooks** (_not_ individual commands) to your `whisky.json` file should be followed by `./vendor/bin/whisky update` to ensure that these changes are reflected in your `.git/hooks` directory.
6465

66+
### Working With Hook Arguments
67+
Some hooks in git are passed arguments.
68+
69+
The `commit-msg` hook is a perfect example. It's passed the path to git's temporary file containing the commit message,
70+
which can then be used by scripts like npm's [commitlint](https://commitlint.js.org/) to allow or prevent commit
71+
messages that might not conform to your project's standards.
72+
73+
To use this argument that git passes, you can _optionally_ include `$1` in your array of commands.
74+
(You should wrap it in escaped double quotes to prevent odd behavior due to whitespace)
75+
76+
```js
77+
// whisky.json
78+
// ...
79+
"commit-msg": [
80+
"npx --no -- commitlint --edit \"$1\""
81+
]
82+
// ...
83+
```
84+
85+
> [!IMPORTANT]
86+
> For `commitlint` specifically, you'll need to follow the instructions in their
87+
> [documentation](https://commitlint.js.org/guides/getting-started.html),
88+
> as it will require extra packages and setup to run in your project.
89+
6590
### Automating Hook Updates
6691
While we suggest leaving Whisky as an 'opt-in' tool, by adding a couple of Composer scripts we can _ensure_ consistent git hooks for all project contributors. This will **force** everyone on the project to use Whisky:
6792

@@ -93,7 +118,8 @@ In this case, running the following command will have the exact same effect.
93118
./vendor/bin/whisky skip-once
94119
```
95120

96-
> **Note** by adding `alias whisky=./vendor/bin/whisky` to your `bash.rc` file, you can shorten the length of this command.
121+
> [!TIP]
122+
> by adding `alias whisky=./vendor/bin/whisky` to your `bash.rc` file, you can shorten the length of this command.
97123
98124
### Disabling Hooks
99125
Adding a hook's name to the `disabled` array in your `whisky.json` will disable the hook from running.
@@ -115,7 +141,8 @@ you to run scripts written in _any_ language.
115141
// ...
116142
```
117143

118-
> **Note** When doing this, make sure any scripts referenced are **executable**:
144+
> [!NOTE]
145+
> When doing this, make sure any scripts referenced are **executable**:
119146
```bash
120147
chmod +x ./scripts/*
121148
```
@@ -142,7 +169,8 @@ whisky uninstall -n
142169

143170

144171
## Contributing
145-
> **Note** Don't build the binary when contributing. The binary will be built when a release is tagged.
172+
> [!NOTE]
173+
> Don't build the binary when contributing. The binary will be built when a release is tagged.
146174
147175
Please see [CONTRIBUTING](CONTRIBUTING.md) for more details.
148176

app/Commands/Run.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
class Run extends Command
1414
{
15-
protected $signature = 'run {hook}';
15+
protected $signature = 'run {hook} {argument?}';
1616

1717
protected $description = 'Run the scripts for a given hook';
1818

@@ -41,6 +41,12 @@ public function handle(): int
4141
Hook::make($this->argument('hook'))
4242
->getScripts()
4343
->each(function (string $script) use (&$exitCode): void {
44+
$script = str_replace(
45+
search: '$1',
46+
replace: trim($this->argument('argument'), '"'),
47+
subject: $script,
48+
);
49+
4450
$isTtySupported = SymfonyProcess::isTtySupported();
4551

4652
$result = $isTtySupported

app/Hook.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ public function getScripts(): Collection
140140
public function getSnippets(): Collection
141141
{
142142
return collect([
143-
"{$this->bin} run {$this->hook}",
143+
"{$this->bin} run {$this->hook} \"$1\"",
144144
// Legacy Snippets.
145+
"{$this->bin} run {$this->hook}",
145146
"eval \"$({$this->bin} get-run-cmd {$this->hook})\"",
146147
"eval \"$(./vendor/bin/whisky get-run-cmd {$this->hook})\"",
147148
]);

app/Platform.php

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public static function cwd(string $path = ''): string
1515
return static::normalizePath(getcwd());
1616
}
1717

18+
public static function temp_test_path(string $path = ''): string
19+
{
20+
if ($path) {
21+
return static::cwd("tests/tmp/{$path}");
22+
}
23+
24+
return static::cwd('tests/tmp');
25+
}
26+
1827
public static function normalizePath(string $path): string
1928
{
2029
if ((new self)->isWindows()) {

tests/Feature/RunTest.php

+121-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use Illuminate\Support\Facades\File;
44
use ProjektGopher\Whisky\Platform;
55

6-
it('deletes skip-once file if exists as long as whisky.json exists', function () {
6+
it('deletes skip-once file if exists as long as whisky.json exists and does not run the hook', function () {
77
File::shouldReceive('missing')
88
->once()
99
->with(Platform::cwd('whisky.json'))
@@ -19,7 +19,125 @@
1919
->with(Platform::cwd('.git/hooks/skip-once'))
2020
->andReturnTrue();
2121

22+
$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
23+
24+
File::shouldReceive('get')
25+
->with(Platform::cwd('whisky.json'))
26+
->andReturn(<<<"JSON"
27+
{
28+
"disabled": [],
29+
"hooks": {
30+
"pre-commit": [
31+
"echo \"poop\" > {$tmp_file}"
32+
]
33+
}
34+
}
35+
JSON);
36+
2237
$this->artisan('run pre-commit')
23-
->doesntExpectOutputToContain('run-hook')
2438
->assertExitCode(0);
25-
})->skip('Needs to be refactored so that the hooks don\'t actually run');
39+
40+
expect(file_exists($tmp_file))
41+
->toBeFalse();
42+
});
43+
44+
it('accepts an optional argument and the argument is correct', function () {
45+
File::shouldReceive('missing')
46+
->with(Platform::cwd('whisky.json'))
47+
->andReturnFalse();
48+
49+
File::shouldReceive('exists')
50+
->with(Platform::cwd('.git/hooks/skip-once'))
51+
->andReturnFalse();
52+
53+
$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
54+
55+
/**
56+
* We need to send the output to the disk
57+
* otherwise the output is sent to the
58+
* test output and we can't check it.
59+
*/
60+
File::shouldReceive('get')
61+
->with(Platform::cwd('whisky.json'))
62+
->andReturn(<<<"JSON"
63+
{
64+
"disabled": [],
65+
"hooks": {
66+
"commit-msg": [
67+
"echo \"$1\" > {$tmp_file}"
68+
]
69+
}
70+
}
71+
JSON);
72+
73+
$this->artisan('run commit-msg ".git/COMMIT_EDITMSG"')
74+
->assertExitCode(0);
75+
76+
expect(file_get_contents($tmp_file))
77+
->toContain('.git/COMMIT_EDITMSG');
78+
79+
unlink($tmp_file);
80+
});
81+
82+
it('still works if no arguments are passed to run command', function () {
83+
File::shouldReceive('missing')
84+
->with(Platform::cwd('whisky.json'))
85+
->andReturnFalse();
86+
87+
File::shouldReceive('exists')
88+
->with(Platform::cwd('.git/hooks/skip-once'))
89+
->andReturnFalse();
90+
91+
$tmp_file = Platform::temp_test_path('whisky_test_pre_commit');
92+
93+
File::shouldReceive('get')
94+
->with(Platform::cwd('whisky.json'))
95+
->andReturn(<<<"JSON"
96+
{
97+
"disabled": [],
98+
"hooks": {
99+
"pre-commit": [
100+
"echo \"pre-commit\" > {$tmp_file}"
101+
]
102+
}
103+
}
104+
JSON);
105+
106+
$this->artisan('run pre-commit')
107+
->assertExitCode(0);
108+
109+
unlink($tmp_file);
110+
});
111+
112+
it('handles a missing expected argument gracefully', function () {
113+
File::shouldReceive('missing')
114+
->with(Platform::cwd('whisky.json'))
115+
->andReturnFalse();
116+
117+
File::shouldReceive('exists')
118+
->with(Platform::cwd('.git/hooks/skip-once'))
119+
->andReturnFalse();
120+
121+
$tmp_file = Platform::temp_test_path('whisky_test_commit_msg');
122+
123+
File::shouldReceive('get')
124+
->with(Platform::cwd('whisky.json'))
125+
->andReturn(<<<"JSON"
126+
{
127+
"disabled": [],
128+
"hooks": {
129+
"commit-msg": [
130+
"echo \"$1\" > {$tmp_file}"
131+
]
132+
}
133+
}
134+
JSON);
135+
136+
$this->artisan('run commit-msg')
137+
->assertExitCode(0);
138+
139+
expect(file_exists($tmp_file))
140+
->toBeTrue();
141+
142+
unlink($tmp_file);
143+
});

tests/tmp/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

0 commit comments

Comments
 (0)