Skip to content
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: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ jobs:
uses: innmind/github-workflows/.github/workflows/psalm-matrix.yml@main
cs:
uses: innmind/github-workflows/.github/workflows/cs.yml@main
with:
php-version: '8.2'
12 changes: 12 additions & 0 deletions .github/workflows/extensive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Extensive CI

on:
push:
tags:
- '*'
paths:
- '.github/workflows/extensive.yml'

jobs:
blackbox:
uses: innmind/github-workflows/.github/workflows/extensive.yml@main
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## [Unreleased]

### Changed

- Requires PHP `8.4`
- Commands name only support alphanumerical characters (use unicode at your own risk)
- `Innmind\CLI\Environment` is now a final class, all previous implementations are now flagged as internal
- Requires `innmind/operating-system:~7.0`

## 4.0.0 - 2025-07-13

### Added
Expand Down
4 changes: 4 additions & 0 deletions blackbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
};

Application::new($argv)
->when(
\getenv('BLACKBOX_SET_SIZE') !== false,
static fn(Application $app) => $app->scenariiPerProof((int) \getenv('BLACKBOX_SET_SIZE')),
)
->disableMemoryLimit()
->when(
\getenv('ENABLE_COVERAGE') !== false,
Expand Down
16 changes: 8 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
"issues": "http://github.com/Innmind/CLI/issues"
},
"require": {
"php": "~8.2",
"innmind/io": "~3.2",
"innmind/immutable": "~5.16",
"innmind/url": "~4.0",
"innmind/operating-system": "~6.0",
"innmind/stack-trace": "~4.0",
"innmind/validation": "~2.0"
"php": "~8.4",
"innmind/io": "~4.0",
"innmind/immutable": "~6.0",
"innmind/url": "~5.0",
"innmind/operating-system": "~7.0",
"innmind/stack-trace": "~5.0",
"innmind/validation": "~3.0"
},
"autoload": {
"psr-4": {
Expand All @@ -34,7 +34,7 @@
}
},
"require-dev": {
"innmind/static-analysis": "^1.2.1",
"innmind/static-analysis": "~1.3",
"innmind/black-box": "^6.4.1",
"innmind/coding-standard": "~2.0"
}
Expand Down
3 changes: 1 addition & 2 deletions fixtures/thrower.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
use Innmind\CLI\{
Main,
Environment,
Exception\LogicException,
};
use Innmind\OperatingSystem\OperatingSystem;
use Innmind\Immutable\Attempt;

new class extends Main {
protected function main(Environment $env, OperatingSystem $os): Attempt
{
throw new LogicException('waaat');
throw new \LogicException('waaat');
}
};
4 changes: 2 additions & 2 deletions src/Command/Usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ public function toString(): string
->arguments
->map(static fn($argument) => ' '.$argument->toString())
->map(Str::of(...))
->fold(new Concat)
->fold(Concat::monoid)
->toString();

if ($this->pack->unwrap()) {
Expand All @@ -369,7 +369,7 @@ public function toString(): string
->options
->map(static fn($argument) => ' '.$argument->toString())
->map(Str::of(...))
->fold(new Concat)
->fold(Concat::monoid)
->toString();
$string .= ' --help --no-interaction';

Expand Down
110 changes: 99 additions & 11 deletions src/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

namespace Innmind\CLI;

use Innmind\CLI\Environment\ExitCode;
use Innmind\CLI\Environment\{
ExitCode,
Implementation,
GlobalEnvironment,
InMemory,
};
use Innmind\IO\IO;
use Innmind\Url\Path;
use Innmind\Immutable\{
Map,
Expand All @@ -16,48 +22,130 @@
/**
* @psalm-immutable
*/
interface Environment
final class Environment
{
private function __construct(
private Implementation $implementation,
) {
}

/**
* @internal
*/
public static function global(IO $io): self
{
return new self(GlobalEnvironment::of($io));
}

/**
* @internal
*
* @param list<string> $input
* @param list<string> $arguments
* @param list<array{string, string}> $variables
*/
public static function inMemory(
array $input,
bool $interactive,
array $arguments,
array $variables,
string $workingDirectory,
): self {
return new self(InMemory::of(
$input,
$interactive,
$arguments,
$variables,
$workingDirectory,
));
}

/**
* True if the environment running the script is an interactive terminal
*/
public function interactive(): bool;
public function interactive(): bool
{
return $this->implementation->interactive();
}

/**
* @param ?positive-int $length
*
* @return array{Attempt<Str>, self}
*/
public function read(?int $length = null): array;
public function read(?int $length = null): array
{
[$read, $implementation] = $this->implementation->read($length);

return [$read, new self($implementation)];
}

/**
* @return Attempt<self>
*/
public function output(Str $data): Attempt;
public function output(Str $data): Attempt
{
return $this
->implementation
->output($data)
->map(static fn($implementation) => new self($implementation));
}

/**
* @return Attempt<self>
*/
public function error(Str $data): Attempt;
public function error(Str $data): Attempt
{
return $this
->implementation
->error($data)
->map(static fn($implementation) => new self($implementation));
}

/**
* @return Sequence<string>
*/
public function arguments(): Sequence;
public function arguments(): Sequence
{
return $this->implementation->arguments();
}

/**
* @return Map<string, string>
*/
public function variables(): Map;
public function variables(): Map
{
return $this->implementation->variables();
}

/**
* @param int<0, 254> $code
*/
public function exit(int $code): self;
public function exit(int $code): self
{
return new self($this->implementation->exit($code));
}

/**
* @return Maybe<ExitCode>
*/
public function exitCode(): Maybe;
public function workingDirectory(): Path;
public function exitCode(): Maybe
{
return $this->implementation->exitCode();
}

public function workingDirectory(): Path
{
return $this->implementation->workingDirectory();
}

/**
* @internal
*
* @return Sequence<array{Str, 'output'|'error'}>
*/
public function outputted(): Sequence
{
return $this->implementation->outputted();
}
}
12 changes: 9 additions & 3 deletions src/Environment/GlobalEnvironment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

namespace Innmind\CLI\Environment;

use Innmind\CLI\Environment;
use Innmind\TimeContinuum\Period;
use Innmind\Time\Period;
use Innmind\IO\{
IO,
Streams\Stream\Read,
Expand All @@ -21,8 +20,9 @@

/**
* @psalm-immutable
* @internal
*/
final class GlobalEnvironment implements Environment
final class GlobalEnvironment implements Implementation
{
/**
* @param Output<'stdout'> $output
Expand Down Expand Up @@ -180,4 +180,10 @@ public function workingDirectory(): Path
{
return $this->workingDirectory;
}

#[\Override]
public function outputted(): Sequence
{
return Sequence::of();
}
}
68 changes: 68 additions & 0 deletions src/Environment/Implementation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
declare(strict_types = 1);

namespace Innmind\CLI\Environment;

use Innmind\Url\Path;
use Innmind\Immutable\{
Map,
Sequence,
Str,
Attempt,
Maybe,
};

/**
* @psalm-immutable
* @internal
*/
interface Implementation
{
/**
* True if the environment running the script is an interactive terminal
*/
public function interactive(): bool;

/**
* @param ?positive-int $length
*
* @return array{Attempt<Str>, self}
*/
public function read(?int $length = null): array;

/**
* @return Attempt<self>
*/
public function output(Str $data): Attempt;

/**
* @return Attempt<self>
*/
public function error(Str $data): Attempt;

/**
* @return Sequence<string>
*/
public function arguments(): Sequence;

/**
* @return Map<string, string>
*/
public function variables(): Map;

/**
* @param int<0, 254> $code
*/
public function exit(int $code): self;

/**
* @return Maybe<ExitCode>
*/
public function exitCode(): Maybe;
public function workingDirectory(): Path;

/**
* @return Sequence<array{Str, 'output'|'error'}>
*/
public function outputted(): Sequence;
}
Loading