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

feat: Add a command to check the requirements #1230

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ SCOPED_BOX_BIN = bin/box.phar
TMP_SCOPED_BOX_BIN = bin/_box.phar
SCOPED_BOX = $(SCOPED_BOX_BIN)
SCOPED_BOX_DEPS = bin/box bin/box.bat $(shell find src res) box.json.dist scoper.inc.php vendor
BOX_EXPECTED_REQUIREMENTS = tests/Build/expected-box-requirements.txt

DEFAULT_STUB = dist/default_stub.php

Expand Down Expand Up @@ -503,6 +504,8 @@ $(SCOPED_BOX_BIN): $(SCOPED_BOX_DEPS)
@# Use parallelization
$(BOX) compile --ansi

$(BOX) check:requirements bin/box.phar $(BOX_EXPECTED_REQUIREMENTS)

rm $(TMP_SCOPED_BOX_BIN) || true
mv -v bin/box.phar $(TMP_SCOPED_BOX_BIN)

Expand Down
1 change: 1 addition & 0 deletions src/Composer/Artifact/ComposerLock.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use function array_map;

/**
* TODO: move it under the Composer namespace.
* @private
*/
final readonly class ComposerLock
Expand Down
7 changes: 6 additions & 1 deletion src/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
namespace KevinGH\Box\Console;

use Fidry\Console\Application\Application as FidryApplication;
use KevinGH\Box\Console\Command\Check\Requirements as CheckRequirements;
use KevinGH\Box\Console\Command\Check\Signature as CheckSignature;
use KevinGH\Box\Console\Command\Compile;
use KevinGH\Box\Console\Command\Composer\ComposerCheckVersion;
use KevinGH\Box\Console\Command\Composer\ComposerVendorDir;
use KevinGH\Box\Console\Command\Diff;
use KevinGH\Box\Console\Command\Extract;
use KevinGH\Box\Console\Command\GenerateDockerFile;
use KevinGH\Box\Console\Command\Generate\DockerFile as GenerateDockerFile;
use KevinGH\Box\Console\Command\Generate\Requirements as GenerateRequirements;
use KevinGH\Box\Console\Command\Info;
use KevinGH\Box\Console\Command\Info\Signature as InfoSignature;
use KevinGH\Box\Console\Command\Namespace_;
Expand Down Expand Up @@ -99,11 +101,14 @@ public function getCommands(): array
new Info('info:general'),
new InfoSignature(),
new CheckSignature(),
new CheckRequirements(),
new Process(),
new Extract(),
new Validate(),
new Verify(),
new GenerateDockerFile(),
new GenerateDockerFile('generate:docker'),
new GenerateRequirements(),
new Namespace_(),
];
}
Expand Down
107 changes: 107 additions & 0 deletions src/Console/Command/Check/Requirements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

/*
* This file is part of the box project.
*
* (c) Kevin Herrera <[email protected]>
* Théo Fidry <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace KevinGH\Box\Console\Command\Check;

use Fidry\Console\Command\Command;
use Fidry\Console\Command\Configuration;
use Fidry\Console\ExitCode;
use Fidry\Console\IO;
use Fidry\FileSystem\FS;
use KevinGH\Box\Phar\PharInfo;
use KevinGH\Box\RequirementChecker\SuccinctRequirementListFactory;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Filesystem\Path;
use function implode;
use function trim;

/**
* @private
*/
final class Requirements implements Command
{
private const PHAR_ARG = 'phar';
private const EXPECTED_REQUIREMENTS_ARG = 'expected-requirements';

public function getConfiguration(): Configuration
{
return new Configuration(
'check:requirements',
'Displays the hash of the signature',
<<<'HELP'
The <info>%command.name%</info> command will check that the requirements of the provided PHAR
matches the list of provided requirements.
The purpose of this command is to check that the PHAR ships the requirement checker and to keep
track of the extensions required.

If what you want to do is check if the current environment satisfies the PHAR
requirements simply execute the PHAR instead.

The requirements should be listed in the file as follows:

```
PHP ^7.2
ext-phar
ext-xml
ext-filter
```
HELP,
[
new InputArgument(
self::PHAR_ARG,
InputArgument::REQUIRED,
'The PHAR file.',
),
new InputArgument(
self::EXPECTED_REQUIREMENTS_ARG,
InputArgument::REQUIRED,
'Path to a file containing a line return separated list of requirements.',
),
],
);
}

public function execute(IO $io): int
{
$pharPath = $io->getTypedArgument(self::PHAR_ARG)->asNonEmptyString();
$expectedRequirementPath = $io->getTypedArgument(self::EXPECTED_REQUIREMENTS_ARG)->asNonEmptyString();

$pharPath = Path::canonicalize($pharPath);

$pharInfo = new PharInfo($pharPath);

$actualRequirements = trim(
implode(
"\n",
SuccinctRequirementListFactory::create($pharInfo->getRequirements()),
),
);
$expectedRequirements = trim(
FS::getFileContents($expectedRequirementPath),
);

if ($expectedRequirements === $actualRequirements) {
return ExitCode::SUCCESS;
}

$differ = new Differ(new UnifiedDiffOutputBuilder());
$result = $differ->diff($expectedRequirements, $actualRequirements);

$io->writeln($result);

return ExitCode::FAILURE;
}
}
2 changes: 1 addition & 1 deletion src/Console/Command/Check/Signature.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
final class Signature implements Command
{
private const PHAR_ARG = 'phar';
private const HASH = 'hash';
private const HASH = 'expected-hash';

public function getConfiguration(): Configuration
{
Expand Down
3 changes: 2 additions & 1 deletion src/Console/Command/Compile.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use KevinGH\Box\Composer\ComposerProcessFactory;
use KevinGH\Box\Composer\Throwable\IncompatibleComposerVersion;
use KevinGH\Box\Configuration\Configuration;
use KevinGH\Box\Console\Command\Generate\DockerFile;
use KevinGH\Box\Console\Logger\CompilerLogger;
use KevinGH\Box\Console\Logger\CompilerPsrLogger;
use KevinGH\Box\Console\MessageRenderer;
Expand Down Expand Up @@ -986,6 +987,6 @@ private function generateDockerFile(IO $io): int

private function getDockerCommand(): Command
{
return $this->getCommandRegistry()->findCommand(GenerateDockerFile::NAME);
return $this->getCommandRegistry()->findCommand(DockerFile::NAME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
* with this source code in the file LICENSE.
*/

namespace KevinGH\Box\Console\Command;
namespace KevinGH\Box\Console\Command\Generate;

use Fidry\Console\Command\CommandAware;
use Fidry\Console\Command\CommandAwareness;
use Fidry\Console\Command\Configuration;
use Fidry\Console\ExitCode;
use Fidry\Console\IO;
use Fidry\FileSystem\FS;
use KevinGH\Box\Console\Command\Compile;
use KevinGH\Box\Console\Command\ConfigOption;
use KevinGH\Box\DockerFileGenerator;
use KevinGH\Box\Phar\PharInfo;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\StringInput;
Expand All @@ -35,7 +38,7 @@
/**
* @private
*/
final class GenerateDockerFile implements CommandAware
final class DockerFile implements CommandAware
{
use CommandAwareness;

Expand All @@ -44,10 +47,14 @@ final class GenerateDockerFile implements CommandAware
private const PHAR_ARG = 'phar';
private const DOCKER_FILE_NAME = 'Dockerfile';

public function __construct(private readonly string $commandName = self::NAME)
{
}

public function getConfiguration(): Configuration
{
return new Configuration(
'docker',
$this->commandName,
'🐳 Generates a Dockerfile for the given PHAR',
'',
[
Expand Down Expand Up @@ -83,7 +90,7 @@ public function execute(IO $io): int
);
$io->newLine();

$requirementsFilePhar = 'phar://'.$pharFilePath.'/.box/.requirements.php';
$requirementsFilePhar = 'phar://'.$pharFilePath.'/'.PharInfo::BOX_REQUIREMENTS;

return $this->generateFile(
$pharFilePath,
Expand Down
71 changes: 71 additions & 0 deletions src/Console/Command/Generate/Requirements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

/*
* This file is part of the box project.
*
* (c) Kevin Herrera <[email protected]>
* Théo Fidry <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace KevinGH\Box\Console\Command\Generate;

use Fidry\Console\Command\Command;
use Fidry\Console\Command\Configuration;
use Fidry\Console\ExitCode;
use Fidry\Console\IO;
use KevinGH\Box\Phar\PharInfo;
use KevinGH\Box\RequirementChecker\SuccinctRequirementListFactory;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Filesystem\Path;

/**
* @private
*/
final class Requirements implements Command
{
private const PHAR_ARG = 'phar';

public function getConfiguration(): Configuration
{
return new Configuration(
'generate:requirements',
'Outputs a succinct list of the PHAR requirements',
<<<'HELP'
The <info>%command.name%</info> command will generate a succinct list of the requirements of the PHAR
if it ships the Box's RequirementChecker.

This command is mostly to generate a list to be able to use it with <info>check:requirements</info> to
keep track of the PHAR requirements.

If what you want to do is check the more detailed list of the requirements of your PHAR use the
<info>info</info>> command instead.
HELP,
[
new InputArgument(
self::PHAR_ARG,
InputArgument::REQUIRED,
'The PHAR file.',
),
],
);
}

public function execute(IO $io): int
{
$pharPath = $io->getTypedArgument(self::PHAR_ARG)->asNonEmptyString();

$pharPath = Path::canonicalize($pharPath);

$pharInfo = new PharInfo($pharPath);
$requirements = $pharInfo->getRequirements();

$io->writeln(SuccinctRequirementListFactory::create($requirements));

return ExitCode::SUCCESS;
}
}
4 changes: 2 additions & 2 deletions src/Console/PharInfoRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
use KevinGH\Box\NotInstantiable;
use KevinGH\Box\Phar\CompressionAlgorithm;
use KevinGH\Box\Phar\PharInfo;
use KevinGH\Box\RequirementChecker\InvalidRequirements;
use KevinGH\Box\RequirementChecker\NoRequirementsFound;
use KevinGH\Box\RequirementChecker\Requirement;
use KevinGH\Box\RequirementChecker\Requirements;
use KevinGH\Box\RequirementChecker\RequirementType;
use KevinGH\Box\RequirementChecker\Throwable\InvalidRequirements;
use KevinGH\Box\RequirementChecker\Throwable\NoRequirementsFound;
use SplFileInfo;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Path;
Expand Down
5 changes: 2 additions & 3 deletions src/Phar/PharInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,10 @@
use Fidry\FileSystem\FS;
use KevinGH\Box\Console\Command\Extract;
use KevinGH\Box\ExecutableFinder;
use KevinGH\Box\Phar\Throwable\InvalidPhar;
use KevinGH\Box\RequirementChecker\InvalidRequirements;
use KevinGH\Box\RequirementChecker\NoRequirementsFound;
use KevinGH\Box\RequirementChecker\Requirement;
use KevinGH\Box\RequirementChecker\Requirements;
use KevinGH\Box\RequirementChecker\Throwable\InvalidRequirements;
use KevinGH\Box\RequirementChecker\Throwable\NoRequirementsFound;
use OutOfBoundsException;
use Phar;
use Symfony\Component\Filesystem\Path;
Expand Down
36 changes: 36 additions & 0 deletions src/RequirementChecker/InvalidRequirements.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

/*
* This file is part of the box project.
*
* (c) Kevin Herrera <[email protected]>
* Théo Fidry <[email protected]>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace KevinGH\Box\RequirementChecker;

use RuntimeException;
use function get_debug_type;
use function sprintf;

/**
* @private
*/
final class InvalidRequirements extends RuntimeException
{
public static function forRequirements(string $file, mixed $value): self
{
return new self(
sprintf(
'Could not interpret Box\'s RequirementChecker shipped in "%s". Expected an array got "%s".',
$file,
get_debug_type($value),
),
);
}
}
Loading
Loading