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

Add check() function to ensure requirements are met #573

Merged
merged 1 commit into from
Nov 15, 2024
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
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
1 change: 1 addition & 0 deletions doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Castor provides the following built-in functions:
- [`app`](going-further/helpers/console-and-io.md#the-app-function)
- [`cache`](going-further/helpers/cache.md#the-cache-function)
- [`capture`](getting-started/run.md#the-capture-function)
- [`check`](going-further/helpers/assertion.md#the-check-function)
- [`context`](getting-started/context.md#the-context-function)
- [`exit_code`](getting-started/run.md#the-exit_code-function)
- [`finder`](going-further/helpers/filesystem.md#the-finder-function)
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
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: 32
Number of PHP files: 33
2 changes: 2 additions & 0 deletions tests/Generated/ListTest.php.output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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
cache:complex Cache with usage of CacheItemInterface
cache:simple Cache a simple call
castor:composer Interact with built-in Composer for castor
Expand Down
16 changes: 2 additions & 14 deletions tools/release/castor.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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 @@ -106,7 +107,7 @@ function release(): int
check(
'Check the number of artifacts',
'There are not enough files in the artifacts directory.',
fn () => EXPECTED_ARTIFACTS === count($files),
fn () => EXPECTED_ARTIFACTS === \count($files),
);

check(
Expand Down Expand Up @@ -139,19 +140,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