Skip to content

Commit

Permalink
Add check() function to ensure requirements are met
Browse files Browse the repository at this point in the history
  • Loading branch information
lyrixx committed Nov 15, 2024
1 parent 8017946 commit ca2ae37
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Features

* Better rendering of run errors
* Add `check()` function to ensure requirements are met
* Add `ProblemException` to handle problems in a more structured way

### Internal

Expand Down
2 changes: 1 addition & 1 deletion doc/_nav.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
- [Home](index.md)
- [Installation](installation.md)
- [Getting started](getting-started/index.md)
- [Installation and Autocomplete](getting-started/installation.md)
- [Basic Usage](getting-started/basic-usage.md)
- [Executing Processes with `run()`](getting-started/run.md)
- [Task Arguments](getting-started/arguments.md)
Expand Down
46 changes: 46 additions & 0 deletions doc/going-further/helpers/assertion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Assertion

## The `check()` function

Castor provides a `check()` function to ensure some requirements are met:

```php
use Castor\Attribute\AsTask;
use Symfony\Component\Process\ExecutableFinder;

use function Castor\check;

#[AsTask()]
function git()
{
check(
'Check if Git is installed',
'Git is not installed. Please install it before.',
fn () => (new ExecutableFinder())->find('git'),
);
}
```

## The `ProblemException` exception

If you must stop a task execution because of a problem, you can throw a
`ProblemException` exception:

```php
use Castor\Attribute\AsTask;
use Castor\Exception\ProblemException;

use function Castor\capture;

#[AsTask()]
function git()
{
if (capture('git status --porcelain')) {
throw new ProblemException('There are uncommitted changes.');
}
}
```

It will stop the execution of the task and display the message in the console.
And it will also return a non-zero exit code (default to 1) to indicate that the
task failed.
3 changes: 2 additions & 1 deletion doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function hello(): void

Will expose a `greetings:hello` task that you can run with `castor greetings:hello`:

```bash
```shell
$ castor greetings:hello
Hello from castor
```
Expand Down Expand Up @@ -86,6 +86,7 @@ function destroy(bool $force = false)

Discover more by reading the docs:

* [Installation](installation.md)
* [Getting started with Castor](getting-started/index.md)
* [Going further with Castor](going-further/index.md)
* [Castor reference](reference.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ ln -s $PWD/bin/castor $HOME/.local/bin/castor

#### Using setup-castor action

Castor provide a [Github Action to install Castor in your workflow](https://github.com/marketplace/actions/setup-castor).
Castor provide a [Github Action to install Castor in your workflow](https://github.com/marketplace/actions/setup-castor).
Here is an example:

```yaml
Expand Down
24 changes: 24 additions & 0 deletions examples/assertion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace assertion;

use Castor\Attribute\AsTask;
use Castor\Exception\ProblemException;

use function Castor\check;

#[AsTask(description: 'Ensure we are in the future')]
function ensure_we_are_in_the_future(): void
{
check(
'Check if we are in the future',
'We are not in the future 😱',
fn () => (!usleep(500_000) && new \DateTime() > new \DateTime('2015-10-21'))
);
}

#[AsTask(description: 'Throws a Problem exception')]
function throw_an_exception(): never
{
throw new ProblemException('Houston, we have a problem');
}
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ parameters:
count: 1
path: examples/args.php

-
message: "#^Negated boolean expression is always true\\.$#"
count: 1
path: examples/assertion.php

-
message: "#^Result of function usleep \\(void\\) is used\\.$#"
count: 1
path: examples/assertion.php

-
message: "#^Class RepackedApplication not found\\.$#"
count: 1
Expand Down
2 changes: 1 addition & 1 deletion src/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function renderThrowable(\Throwable $e, OutputInterface $output): void
$process = $e->getProcess();
$runnable = $this->processRunner->buildRunnableCommand($process);

$this->io->writeln(sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))));
$this->io->writeln(\sprintf('<comment>%s</comment>', OutputFormatter::escape(\sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))));
$this->io->error('The following process did not finish successfully (exit code ' . $process->getExitCode() . '):');
$this->io->writeln("<fg=yellow>{$runnable}</>");
$this->io->newLine();
Expand Down
17 changes: 17 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Castor\Console\Application;
use Castor\Exception\ExecutableNotFoundException;
use Castor\Exception\MinimumVersionRequirementNotMetException;
use Castor\Exception\ProblemException;
use Castor\Exception\WaitFor\ExitedBeforeTimeoutException;
use Castor\Exception\WaitFor\TimeoutReachedException;
use Castor\Helper\HasherHelper;
Expand Down Expand Up @@ -160,6 +161,22 @@ function exit_code(
;
}

/**
* @param callable():bool $check
*/
function check(string $title, string $failureMessage, callable $check): void
{
io()->write($title);

if (!$check()) {
io()->writeln('');

throw new ProblemException($failureMessage);
}

io()->writeln('');
}

/**
* @deprecated Since castor/castor 0.8. Use Castor\exit_code() instead
*/
Expand Down
22 changes: 22 additions & 0 deletions tests/Generated/AssertionEnsureWeAreInTheFutureTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Castor\Tests\Generated;

use Castor\Tests\TaskTestCase;
use Symfony\Component\Process\Exception\ProcessFailedException;

class AssertionEnsureWeAreInTheFutureTest extends TaskTestCase
{
// assertion:ensure-we-are-in-the-future
public function test(): void
{
$process = $this->runTask(['assertion:ensure-we-are-in-the-future']);

if (0 !== $process->getExitCode()) {
throw new ProcessFailedException($process);
}

$this->assertStringEqualsFile(__FILE__ . '.output.txt', $process->getOutput());
$this->assertSame('', $process->getErrorOutput());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Check if we are in the future ✅
22 changes: 22 additions & 0 deletions tests/Generated/AssertionThrowAnExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Castor\Tests\Generated;

use Castor\Tests\TaskTestCase;
use Symfony\Component\Process\Exception\ProcessFailedException;

class AssertionThrowAnExceptionTest extends TaskTestCase
{
// assertion:throw-an-exception
public function test(): void
{
$process = $this->runTask(['assertion:throw-an-exception']);

if (1 !== $process->getExitCode()) {
throw new ProcessFailedException($process);
}

$this->assertStringEqualsFile(__FILE__ . '.output.txt', $process->getOutput());
$this->assertSame('', $process->getErrorOutput());
}
}
2 changes: 2 additions & 0 deletions tests/Generated/AssertionThrowAnExceptionTest.php.output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[ERROR] Houston, we have a problem

2 changes: 1 addition & 1 deletion tests/Generated/FilesystemFindTest.php.output.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Number of PHP files: 34
Number of PHP files: 35
2 changes: 2 additions & 0 deletions tests/Generated/ListTest.php.output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ args:another-args Dumps all argu
args:args Dumps all arguments and options, with custom configuration
args:autocomplete-argument Provides autocomplete for an argument
args:passthru Dumps all arguments and options, without configuration nor validation
assertion:ensure-we-are-in-the-future Ensure we are in the future
assertion:throw-an-exception Throws a Problem exception
bar:bar Prints bar, but also executes foo
cache:complex Cache with usage of CacheItemInterface
cache:simple Cache a simple call
Expand Down
3 changes: 2 additions & 1 deletion tests/Monolog/Processor/ProcessProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public function test(): void
$mock = $this->getMockBuilder(ProcessRunner::class)
->onlyMethods(['buildRunnableCommand'])
->disableOriginalConstructor()
->getMock();
->getMock()
;

$processor = new ProcessProcessor($mock);

Expand Down
14 changes: 1 addition & 13 deletions tools/release/castor.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Process\ExecutableFinder;

use function Castor\capture;
use function Castor\check;
use function Castor\context;
use function Castor\finder;
use function Castor\fs;
Expand Down Expand Up @@ -137,19 +138,6 @@ function release(): int
return 0;
}

function check(string $title, string $failureMessage, callable $check): void
{
io()->write($title);

if (!$check()) {
io()->writeln('');

throw new ProblemException($failureMessage);
}

io()->writeln('');
}

function checkCi(array $run): void
{
io()->comment("{$run['name']}'s run id <comment>{$run['databaseId']}</comment>");
Expand Down

0 comments on commit ca2ae37

Please sign in to comment.