mirror of
https://github.com/opdavies/versa.git
synced 2025-01-22 11:37:33 +00:00
Add and use an action to get the project language
This commit is contained in:
parent
8c9f36151b
commit
ccbae0ed36
6
.env.test
Normal file
6
.env.test
Normal file
|
@ -0,0 +1,6 @@
|
|||
# define your env variables for the test env here
|
||||
KERNEL_CLASS='App\Kernel'
|
||||
APP_SECRET='$ecretf0rt3st'
|
||||
SYMFONY_DEPRECATIONS_HELPER=999999
|
||||
PANTHER_APP_ENV=panther
|
||||
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
/bin/
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
/.env.local
|
||||
|
@ -16,3 +17,13 @@
|
|||
###> phpstan/phpstan ###
|
||||
phpstan.neon
|
||||
###< phpstan/phpstan ###
|
||||
|
||||
###> symfony/phpunit-bridge ###
|
||||
.phpunit.result.cache
|
||||
/phpunit.xml
|
||||
###< symfony/phpunit-bridge ###
|
||||
|
||||
###> phpunit/phpunit ###
|
||||
/phpunit.xml
|
||||
.phpunit.result.cache
|
||||
###< phpunit/phpunit ###
|
||||
|
|
|
@ -8,6 +8,10 @@ flake:
|
|||
- php82
|
||||
- php82Packages.composer
|
||||
|
||||
git:
|
||||
ignore:
|
||||
- /bin/
|
||||
|
||||
# experimental:
|
||||
# TODO: not yet supported in Symfony projects?
|
||||
# createInclusiveGitIgnoreFile: true
|
||||
|
|
|
@ -70,6 +70,10 @@
|
|||
"bamarni/composer-bin-plugin": "^1.8",
|
||||
"phpstan/extension-installer": "^1.3",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpstan/phpstan-strict-rules": "^1.5"
|
||||
"phpstan/phpstan-strict-rules": "^1.5",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"symfony/browser-kit": "7.0.*",
|
||||
"symfony/css-selector": "7.0.*",
|
||||
"symfony/phpunit-bridge": "^7.0"
|
||||
}
|
||||
}
|
||||
|
|
2084
composer.lock
generated
2084
composer.lock
generated
File diff suppressed because it is too large
Load diff
2
notes
2
notes
|
@ -1,4 +1,4 @@
|
|||
Keep adding JS/TS support
|
||||
Review https://github.com/phpstan/phpstan-symfony
|
||||
Add tests for determining the correct project language and type based on present files
|
||||
Add tests for determining the correct project type based on present files
|
||||
Add documentation for each project language and type - table of command that are run?
|
||||
|
|
38
phpunit.xml.dist
Normal file
38
phpunit.xml.dist
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
convertDeprecationsToExceptions="false"
|
||||
>
|
||||
<php>
|
||||
<ini name="display_errors" value="1" />
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<server name="APP_ENV" value="test" force="true" />
|
||||
<server name="SHELL_VERBOSITY" value="-1" />
|
||||
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
|
||||
<server name="SYMFONY_PHPUNIT_VERSION" value="9.6" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Project Test Suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
|
||||
<listeners>
|
||||
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
|
||||
</listeners>
|
||||
|
||||
<extensions>
|
||||
</extensions>
|
||||
</phpunit>
|
36
src/Action/DetermineProjectLanguage.php
Normal file
36
src/Action/DetermineProjectLanguage.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Enum\ProjectLanguage;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
final class DetermineProjectLanguage
|
||||
{
|
||||
public function __construct(
|
||||
private Filesystem $filesystem,
|
||||
private string $workingDir = '.',
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getLanguage(): string
|
||||
{
|
||||
if ($this->filesystem->exists($this->workingDir.'/composer.json')) {
|
||||
return ProjectLanguage::PHP->value;
|
||||
}
|
||||
|
||||
if ($this->filesystem->exists($this->workingDir.'/package.json')) {
|
||||
return ProjectLanguage::JavaScript->value;
|
||||
}
|
||||
|
||||
// TODO: What to do if a project contains multiple languages?
|
||||
// e.g. a composer.lock file (PHP) and pnpm-lock.yaml file (JS)?
|
||||
|
||||
// TODO: validate the language is an allowed value.
|
||||
|
||||
return ProjectLanguage::PHP->value;
|
||||
}
|
||||
}
|
|
@ -4,9 +4,17 @@ namespace App\Console\Command;
|
|||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
abstract class AbstractCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
string $name,
|
||||
protected Filesystem $filesystem,
|
||||
) {
|
||||
parent::__construct(name: $name);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Action\DetermineProjectLanguage;
|
||||
use App\Enum\ProjectLanguage;
|
||||
use App\Enum\ProjectType;
|
||||
use App\Process\Process;
|
||||
|
@ -15,20 +16,22 @@ final class BuildCommand extends AbstractCommand
|
|||
{
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$projectLanguage = null;
|
||||
$projectType = null;
|
||||
|
||||
$extraArgs = $input->getOption('extra-args');
|
||||
$workingDir = $input->getOption('working-dir');
|
||||
|
||||
$language = $input->getOption('language') ?? (new DetermineProjectLanguage(
|
||||
filesystem: $this->filesystem,
|
||||
workingDir: $workingDir,
|
||||
))->getLanguage();
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
// Attempt to prepopulate some of the options, such as the project type
|
||||
// based on its dependencies.
|
||||
// TODO: move this logic to a service so it can be tested.
|
||||
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||
$projectLanguage = ProjectLanguage::PHP->value;
|
||||
|
||||
if ($language === ProjectLanguage::PHP->value) {
|
||||
$json = json_decode(
|
||||
json: strval(file_get_contents($workingDir.'/composer.json')),
|
||||
associative: true,
|
||||
|
@ -44,18 +47,17 @@ final class BuildCommand extends AbstractCommand
|
|||
$projectType = ProjectType::Symfony->value;
|
||||
}
|
||||
} elseif ($filesystem->exists($workingDir.'/fractal.config.js')) {
|
||||
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||
$language = ProjectLanguage::JavaScript->value;
|
||||
$projectType = ProjectType::Fractal->value;
|
||||
}
|
||||
|
||||
// Even if the project language or type is found automatically, still
|
||||
// override it with the option value if there is one.
|
||||
$projectLanguage = $input->getOption('language') ?? $projectLanguage;
|
||||
// Even if the project type is found automatically, still override it
|
||||
// with the option value if there is one.
|
||||
$projectType = $input->getOption('type') ?? $projectType;
|
||||
|
||||
$isDockerCompose = $filesystem->exists($workingDir . '/docker-compose.yaml');
|
||||
|
||||
switch ($projectLanguage) {
|
||||
switch ($language) {
|
||||
case ProjectLanguage::PHP->value:
|
||||
switch ($projectType) {
|
||||
case ProjectType::Drupal->value:
|
||||
|
@ -83,7 +85,7 @@ final class BuildCommand extends AbstractCommand
|
|||
|
||||
$process->run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case ProjectLanguage::JavaScript->value:
|
||||
switch ($projectType) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Action\DetermineProjectLanguage;
|
||||
use App\Enum\ProjectLanguage;
|
||||
use App\Process\Process;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
@ -16,10 +17,10 @@ final class InstallCommand extends AbstractCommand
|
|||
$extraArgs = $input->getOption('extra-args');
|
||||
$workingDir = $input->getOption('working-dir');
|
||||
|
||||
// TODO: What to do if a project contains multiple languages?
|
||||
// e.g. a composer.lock file (PHP) and pnpm-lock.yaml file (JS)?
|
||||
|
||||
// TODO: validate the language is an allowed value.
|
||||
$language = $input->getOption('language') ?? (new DetermineProjectLanguage(
|
||||
filesystem: $this->filesystem,
|
||||
workingDir: $workingDir,
|
||||
))->getLanguage();
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
|
@ -27,7 +28,7 @@ final class InstallCommand extends AbstractCommand
|
|||
$process = Process::create(
|
||||
command: $this->getCommand(
|
||||
filesystem: $filesystem,
|
||||
language: $this->getProjectLanguage($filesystem, $workingDir, $input),
|
||||
language: $language,
|
||||
workingDir: $workingDir,
|
||||
),
|
||||
extraArgs: explode(separator: ' ', string: $extraArgs),
|
||||
|
@ -60,23 +61,4 @@ final class InstallCommand extends AbstractCommand
|
|||
|
||||
return ['composer', 'install'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Filesystem $filesystem
|
||||
* @param non-empty-string $workingDir
|
||||
* @param InputInterface $input
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function getProjectLanguage(Filesystem $filesystem, string $workingDir, InputInterface $input): string {
|
||||
$projectLanguage = null;
|
||||
|
||||
// Determine the language based on the files.
|
||||
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||
$projectLanguage = ProjectLanguage::PHP->value;
|
||||
} elseif ($filesystem->exists($workingDir.'/package.json')) {
|
||||
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||
}
|
||||
|
||||
return $input->getOption('language') ?? $projectLanguage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Action\DetermineProjectLanguage;
|
||||
use App\Enum\ProjectLanguage;
|
||||
use App\Enum\ProjectType;
|
||||
use App\Process\Process;
|
||||
|
@ -14,7 +15,6 @@ final class RunCommand extends AbstractCommand
|
|||
{
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$projectLanguage = null;
|
||||
$projectType = null;
|
||||
|
||||
$extraArgs = $input->getOption('extra-args');
|
||||
|
@ -22,12 +22,15 @@ final class RunCommand extends AbstractCommand
|
|||
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
$language = $input->getOption('language') ?? (new DetermineProjectLanguage(
|
||||
filesystem: $this->filesystem,
|
||||
workingDir: $workingDir,
|
||||
))->getLanguage();
|
||||
|
||||
// Attempt to prepopulate some of the options, such as the project type
|
||||
// based on its dependencies.
|
||||
// TODO: move this logic to a service so it can be tested.
|
||||
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||
$projectLanguage = ProjectLanguage::PHP->value;
|
||||
|
||||
if ($language === ProjectLanguage::PHP->value) {
|
||||
$json = json_decode(
|
||||
json: strval(file_get_contents($workingDir.'/composer.json')),
|
||||
associative: true,
|
||||
|
@ -42,17 +45,14 @@ final class RunCommand extends AbstractCommand
|
|||
} elseif (in_array(needle: 'symfony/framework-bundle', haystack: $dependencies, strict: true)) {
|
||||
$projectType = ProjectType::Symfony->value;
|
||||
}
|
||||
} elseif ($filesystem->exists($workingDir.'/package.json')) {
|
||||
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||
|
||||
} elseif ($language === ProjectLanguage::JavaScript->value) {
|
||||
if ($filesystem->exists($workingDir.'/fractal.config.js')) {
|
||||
$projectType = ProjectType::Fractal->value;
|
||||
}
|
||||
}
|
||||
|
||||
// Even if the project type is found automatically, still override it with
|
||||
// the option value if there is one.
|
||||
$projectLanguage = $input->getOption('language') ?? $projectLanguage;
|
||||
// Even if the project type is found automatically, still override it
|
||||
// with the option value if there is one.
|
||||
$projectType = $input->getOption('type') ?? $projectType;
|
||||
|
||||
$filesystem = new Filesystem();
|
||||
|
|
|
@ -14,6 +14,8 @@ final class TestCommand extends AbstractCommand
|
|||
$extraArgs = $input->getOption('extra-args');
|
||||
$workingDir = $input->getOption('working-dir');
|
||||
|
||||
// TODO: add support for node and jest.
|
||||
|
||||
// TODO: move this logic to a service so it can be tested.
|
||||
$json = json_decode(
|
||||
json: strval(file_get_contents($workingDir.'/composer.json')),
|
||||
|
|
29
symfony.lock
29
symfony.lock
|
@ -11,6 +11,20 @@
|
|||
"phpstan.dist.neon"
|
||||
]
|
||||
},
|
||||
"phpunit/phpunit": {
|
||||
"version": "9.6",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "9.6",
|
||||
"ref": "7364a21d87e658eb363c5020c072ecfdc12e2326"
|
||||
},
|
||||
"files": [
|
||||
".env.test",
|
||||
"phpunit.xml.dist",
|
||||
"tests/bootstrap.php"
|
||||
]
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "7.0",
|
||||
"recipe": {
|
||||
|
@ -54,6 +68,21 @@
|
|||
"src/Kernel.php"
|
||||
]
|
||||
},
|
||||
"symfony/phpunit-bridge": {
|
||||
"version": "7.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.3",
|
||||
"ref": "a411a0480041243d97382cac7984f7dce7813c08"
|
||||
},
|
||||
"files": [
|
||||
".env.test",
|
||||
"bin/phpunit",
|
||||
"phpunit.xml.dist",
|
||||
"tests/bootstrap.php"
|
||||
]
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "7.0",
|
||||
"recipe": {
|
||||
|
|
58
tests/ProjectLanguageTest.php
Normal file
58
tests/ProjectLanguageTest.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
use App\Action\DetermineProjectLanguage;
|
||||
use App\Enum\ProjectLanguage;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
final class ProjectLanguageTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @test
|
||||
* @testdox It identifies a PHP project if it has a composer.json file
|
||||
*/
|
||||
public function it_identifies_a_php_project_if_it_has_a_composer_json_file(): void
|
||||
{
|
||||
$filesystem = $this->createMock(Filesystem::class);
|
||||
$filesystem
|
||||
->method('exists')
|
||||
->with('./composer.json')
|
||||
->willReturn(true);
|
||||
|
||||
$action = new DetermineProjectLanguage(
|
||||
filesystem: $filesystem,
|
||||
);
|
||||
|
||||
self::assertSame(
|
||||
actual: $action->getLanguage(),
|
||||
expected: ProjectLanguage::PHP->value,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @testdox It identifies a node project if it has a package.json file
|
||||
*/
|
||||
public function it_identifies_a_node_project_if_it_has_a_package_json_file(): void
|
||||
{
|
||||
// self::markTestSkipped();
|
||||
|
||||
$filesystem = $this->createMock(Filesystem::class);
|
||||
|
||||
$filesystem
|
||||
->method('exists')
|
||||
->will(self::returnValueMap([
|
||||
['./composer.json', false],
|
||||
['./package.json', true],
|
||||
]));
|
||||
|
||||
$action = new DetermineProjectLanguage(
|
||||
filesystem: $filesystem,
|
||||
);
|
||||
|
||||
self::assertSame(
|
||||
actual: $action->getLanguage(),
|
||||
expected: ProjectLanguage::JavaScript->value,
|
||||
);
|
||||
}
|
||||
}
|
13
tests/bootstrap.php
Normal file
13
tests/bootstrap.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
if (method_exists(Dotenv::class, 'bootEnv')) {
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
}
|
||||
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
umask(0000);
|
||||
}
|
10
versa
10
versa
|
@ -8,14 +8,16 @@ use App\Console\Command\InstallCommand;
|
|||
use App\Console\Command\RunCommand;
|
||||
use App\Console\Command\TestCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
$application = new Application();
|
||||
$filesystem = new Filesystem();
|
||||
|
||||
$application->addCommands([
|
||||
new BuildCommand(name: 'build'),
|
||||
new InstallCommand(name: 'install'),
|
||||
new RunCommand(name: 'run'),
|
||||
new TestCommand(name: 'test'),
|
||||
new BuildCommand(filesystem: $filesystem, name: 'build'),
|
||||
new InstallCommand(filesystem: $filesystem, name: 'install'),
|
||||
new RunCommand(filesystem: $filesystem, name: 'run'),
|
||||
new TestCommand(filesystem: $filesystem, name: 'test'),
|
||||
]);
|
||||
|
||||
$application->run();
|
||||
|
|
Loading…
Reference in a new issue