From e1f801de5a3c5af9e544ceac3d2b82a9dfedeebb Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 09:15:45 +0200 Subject: [PATCH 1/8] feat: create bundle and extension --- composer.json | 10 ++-- src/.gitkeep | 0 src/DependencyInjection/BiomeJsExtension.php | 53 ++++++++++++++++++++ src/KocalBiomeJsBundle.php | 17 +++++++ 4 files changed, 77 insertions(+), 3 deletions(-) delete mode 100644 src/.gitkeep create mode 100644 src/DependencyInjection/BiomeJsExtension.php create mode 100644 src/KocalBiomeJsBundle.php diff --git a/composer.json b/composer.json index 1fd7275..1d8e338 100644 --- a/composer.json +++ b/composer.json @@ -21,11 +21,15 @@ } ], "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0" }, "require-dev": { "phpstan/phpstan": "^1.11", - "symplify/easy-coding-standard": "^12.1", - "phpunit/phpunit": "^10.5 || ^11.1" + "phpunit/phpunit": "^10.5 || ^11.1", + "symplify/easy-coding-standard": "^12.1.2", + "symfony/framework-bundle": "^6.4|^7.0" } } diff --git a/src/.gitkeep b/src/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/DependencyInjection/BiomeJsExtension.php b/src/DependencyInjection/BiomeJsExtension.php new file mode 100644 index 0000000..d82d77f --- /dev/null +++ b/src/DependencyInjection/BiomeJsExtension.php @@ -0,0 +1,53 @@ +load('services.php'); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + } + + public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface + { + return $this; + } + + public function getAlias(): string + { + return 'kocal_biome_js'; + } + + public function getConfigTreeBuilder(): TreeBuilder + { + $treeBuilder = new TreeBuilder($this->getAlias()); + $rootNode = $treeBuilder->getRootNode(); + \assert($rootNode instanceof ArrayNodeDefinition); + + $rootNode + ->children() + ->scalarNode('binary_version') + ->info('Biome.js CLI version to download - null means the latest version') + ->example('v1.7.3') + ->defaultNull() + ->end() + ->end(); + + return $treeBuilder; + } +} diff --git a/src/KocalBiomeJsBundle.php b/src/KocalBiomeJsBundle.php new file mode 100644 index 0000000..c67e115 --- /dev/null +++ b/src/KocalBiomeJsBundle.php @@ -0,0 +1,17 @@ + Date: Fri, 17 May 2024 09:16:22 +0200 Subject: [PATCH 2/8] feat: create BiomeJs, BiomeJsBinary, and check/ci commands --- config/services.php | 37 ++++++ src/BiomeJs.php | 113 ++++++++++++++++ src/BiomeJsBinary.php | 131 +++++++++++++++++++ src/Command/BiomeJsCheckCommand.php | 75 +++++++++++ src/Command/BiomeJsCiCommand.php | 70 ++++++++++ src/DependencyInjection/BiomeJsExtension.php | 3 + 6 files changed, 429 insertions(+) create mode 100644 config/services.php create mode 100644 src/BiomeJs.php create mode 100644 src/BiomeJsBinary.php create mode 100644 src/Command/BiomeJsCheckCommand.php create mode 100644 src/Command/BiomeJsCiCommand.php diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..f16c680 --- /dev/null +++ b/config/services.php @@ -0,0 +1,37 @@ +services() + ->set('biomejs.binary', BiomeJsBinary::class) + ->args([ + param('kernel.project_dir'), + param('kernel.project_dir').'/var/biomejs', + abstract_arg('Biome.js binary version'), + ]) + + ->set('biomejs', BiomeJs::class) + ->args([ + service('biomejs.binary'), + ]) + + ->set('biomejs.command.ci', BiomeJsCiCommand::class) + ->args([ + service('biomejs') + ]) + ->tag('console.command') + + ->set('biomejs.command.check', BiomeJsCheckCommand::class) + ->args([ + service('biomejs'), + ]) + ->tag('console.command'); +}; \ No newline at end of file diff --git a/src/BiomeJs.php b/src/BiomeJs.php new file mode 100644 index 0000000..e0f08bf --- /dev/null +++ b/src/BiomeJs.php @@ -0,0 +1,113 @@ +output = $output; + } + + /** + * @param array $path + */ + public function check( + bool $apply, + bool $applyUnsafe, + bool $formatterEnabled, + bool $linterEnabled, + bool $organizeImportsEnabled, + bool $staged, + bool $changed, + string|null $since, + array $path, + ): Process { + $arguments = []; + if ($apply) { + $arguments[] = '--apply'; + } + if ($applyUnsafe) { + $arguments[] = '--apply-unsafe'; + } + $arguments[] = '--formatter-enabled=' . ($formatterEnabled ? 'true' : 'false'); + $arguments[] = '--linter-enabled=' . ($linterEnabled ? 'true' : 'false'); + $arguments[] = '--organize-imports-enabled=' . ($organizeImportsEnabled ? 'true' : 'false'); + if ($staged) { + $arguments[] = '--staged'; + } + if ($changed) { + $arguments[] = '--changed'; + } + if ($since) { + $arguments[] = '--since=' . $since; + } + $arguments = ['check', ...$arguments, ...$path]; + + $this->biomeJsBinary->setOutput($this->output); + $process = $this->biomeJsBinary->createProcess($arguments); + $process->setTty(true); + + $this->output?->note('Executing Biome.js "check" (pass -v to see more details).'); + if ($this->output?->isVerbose()) { + $this->output->writeln([ + ' Command:', + ' ' . $process->getCommandLine(), + ]); + } + $process->start(); + + return $process; + } + + /** + * @param array $path + */ + public function ci( + bool $formatterEnabled, + bool $linterEnabled, + bool $organizeImportsEnabled, + bool $changed, + string|null $since, + array $path, + ): Process { + $arguments = []; + $arguments[] = '--formatter-enabled=' . ($formatterEnabled ? 'true' : 'false'); + $arguments[] = '--linter-enabled=' . ($linterEnabled ? 'true' : 'false'); + $arguments[] = '--organize-imports-enabled=' . ($organizeImportsEnabled ? 'true' : 'false'); + if ($changed) { + $arguments[] = '--changed'; + } + if ($since) { + $arguments[] = '--since=' . $since; + } + $arguments = ['ci', ...$arguments, ...$path]; + + $this->biomeJsBinary->setOutput($this->output); + $process = $this->biomeJsBinary->createProcess($arguments); + $process->setTty(true); + + $this->output?->note('Executing Biome.js "ci" (pass -v to see more details).'); + if ($this->output?->isVerbose()) { + $this->output->writeln([ + ' Command:', + ' ' . $process->getCommandLine(), + ]); + } + $process->start(); + + return $process; + } +} diff --git a/src/BiomeJsBinary.php b/src/BiomeJsBinary.php new file mode 100644 index 0000000..8d1ea92 --- /dev/null +++ b/src/BiomeJsBinary.php @@ -0,0 +1,131 @@ +httpClient = $httpClient ?? HttpClient::create(); + } + + public function setOutput(SymfonyStyle|null $output): void + { + $this->output = $output; + } + + /** + * @param array $arguments + * + * @throws \Exception + */ + public function createProcess(array $arguments = []): Process + { + $binary = $this->binaryDownloadDir . '/' . $this->getVersion() . '/' . self::getBinaryName(); + if (!is_file($binary)) { + $this->downloadExecutable(); + } + + return new Process([$binary, ...$arguments], $this->cwd); + } + + private function downloadExecutable(): void + { + $url = sprintf('https://github.com/biomejs/biome/releases/download/cli/%s/%s', $this->getVersion(), self::getBinaryName()); + + $this->output?->note(sprintf('Downloading Biome.js binary from %s', $url)); + + if (!is_dir($downloadDir = $this->binaryDownloadDir . '/' . $this->getVersion())) { + mkdir($downloadDir, 0777, true); + } + + $targetPath = $downloadDir . '/' . self::getBinaryName(); + $progressBar = null; + + $response = $this->httpClient->request('GET', $url, [ + 'on_progress' => function (int $dlNow, int $dlSize, array $info) use (&$progressBar): void { + // dlSize is not known at the start + if (0 === $dlSize) { + return; + } + + if (!$progressBar) { + $progressBar = $this->output?->createProgressBar($dlSize); + } + + $progressBar?->setProgress($dlNow); + }, + ]); + $fileHandler = fopen($targetPath, 'w'); + if (!is_resource($fileHandler)) { + throw new \Exception(sprintf('Cannot open file "%s" for writing.', $targetPath)); + } + foreach ($this->httpClient->stream($response) as $chunk) { + fwrite($fileHandler, $chunk->getContent()); + } + fclose($fileHandler); + $progressBar?->finish(); + $this->output?->writeln(''); + // make file executable + chmod($targetPath, 0777); + } + + private function getVersion(): string + { + return $this->cachedVersion ??= $this->binaryVersion ?? $this->getLatestVersion(); + } + + private function getLatestVersion(): string + { + try { + $response = $this->httpClient->request('GET', 'https://api.github.com/repos/biomejs/biome/releases/latest'); + + return str_replace('cli/', '', $response->toArray()['tag_name'] ?? throw new \Exception('Cannot get the latest version name from response JSON.')); + } catch (\Throwable $e) { + throw new \Exception('Cannot determine latest Biome.js binary version. Please specify a version in the configuration.', previous: $e); + } + } + + /** + * @internal + */ + public static function getBinaryName(): string + { + $os = strtolower(\PHP_OS); + $machine = strtolower(php_uname('m')); + + return match (true) { + str_contains($os, 'darwin') => match ($machine) { + 'arm64' => 'biome-darwin-arm64', + 'x86_64' => 'biome-darwin-x64', + default => throw new \Exception(sprintf('No matching machine found for Darwin platform (Machine: %s).', $machine)), + }, + str_contains($os, 'linux') => match ($machine) { + 'arm64', 'aarch64' => 'biome-linux-arm64', + 'x86_64' => 'biome-linux-x64', + default => throw new \Exception(sprintf('No matching machine found for Linux platform (Machine: %s).', $machine)), + }, + str_contains($os, 'win') => match ($machine) { + 'arm64' => 'biome-win32-arm64.exe', + 'x86_64', 'amd64' => 'biome-win32-x64.exe', + default => throw new \Exception(sprintf('No matching machine found for Windows platform (Machine: %s).', $machine)), + }, + default => throw new \Exception(sprintf('Unknown platform or architecture (OS: %s, Machine: %s).', $os, $machine)), + }; + } +} diff --git a/src/Command/BiomeJsCheckCommand.php b/src/Command/BiomeJsCheckCommand.php new file mode 100644 index 0000000..44ca8c3 --- /dev/null +++ b/src/Command/BiomeJsCheckCommand.php @@ -0,0 +1,75 @@ +addOption('apply', null, InputOption::VALUE_NONE, 'Apply safe fixes, formatting and import sorting') + ->addOption('apply-unsafe', null, InputOption::VALUE_NONE, 'Apply safe fixes and unsafe fixes, formatting and import sorting') + ->addOption('formatter-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the formatter check', true) + ->addOption('linter-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the linter check', true) + ->addOption('organize-imports-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the organize imports.', true) + ->addOption('staged', null, InputOption::VALUE_NONE, 'When set to true, only the files that have been staged (the ones prepared to be committed) will be linted.') + ->addOption('changed', null, InputOption::VALUE_NONE, 'When set to true, only the files that have been changed compared to your `defaultBranch` configuration will be linted.') + ->addOption('since', null, InputOption::VALUE_OPTIONAL, "Use this to specify the base branch to compare against when you're using the --changed flag and the `defaultBranch` is not set in your biome.json.") + ->addArgument('path', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Single file, single path or list of paths'); + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->io = new SymfonyStyle($input, $output); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->biomeJs->setOutput($this->io); + + $process = $this->biomeJs->check( + apply: $input->getOption('apply'), + applyUnsafe: $input->getOption('apply-unsafe'), + formatterEnabled: $input->getOption('formatter-enabled'), + linterEnabled: $input->getOption('linter-enabled'), + organizeImportsEnabled: $input->getOption('organize-imports-enabled'), + staged: $input->getOption('staged'), + changed: $input->getOption('changed'), + since: $input->getOption('since'), + path: $input->getArgument('path'), + ); + + $process->wait(fn ($type, $buffer) => $this->io->write($buffer)); + + if (!$process->isSuccessful()) { + $this->io->error('Biome.js check failed: see output above.'); + + return self::FAILURE; + } + + return self::SUCCESS; + } +} diff --git a/src/Command/BiomeJsCiCommand.php b/src/Command/BiomeJsCiCommand.php new file mode 100644 index 0000000..d20e06a --- /dev/null +++ b/src/Command/BiomeJsCiCommand.php @@ -0,0 +1,70 @@ +addOption('formatter-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the formatter check', true) + ->addOption('linter-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the linter check', true) + ->addOption('organize-imports-enabled', null, InputOption::VALUE_OPTIONAL, 'Allow to enable or disable the organize imports.', true) + ->addOption('changed', null, InputOption::VALUE_NONE, 'When set to true, only the files that have been changed compared to your `defaultBranch` configuration will be linted.') + ->addOption('since', null, InputOption::VALUE_OPTIONAL, "Use this to specify the base branch to compare against when you're using the --changed flag and the `defaultBranch` is not set in your biome.json.") + ->addArgument('path', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'Single file, single path or list of paths') + ; + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + $this->io = new SymfonyStyle($input, $output); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->biomeJs->setOutput($this->io); + + $process = $this->biomeJs->ci( + formatterEnabled: $input->getOption('formatter-enabled'), + linterEnabled: $input->getOption('linter-enabled'), + organizeImportsEnabled: $input->getOption('organize-imports-enabled'), + changed: $input->getOption('changed'), + since: $input->getOption('since'), + path: $input->getArgument('path'), + ); + + $process->wait(fn ($type, $buffer) => $this->io->write($buffer)); + + if (!$process->isSuccessful()) { + $this->io->error('Biome.js ci failed: see output above.'); + + return self::FAILURE; + } + + return self::SUCCESS; + } +} diff --git a/src/DependencyInjection/BiomeJsExtension.php b/src/DependencyInjection/BiomeJsExtension.php index d82d77f..bbbc736 100644 --- a/src/DependencyInjection/BiomeJsExtension.php +++ b/src/DependencyInjection/BiomeJsExtension.php @@ -21,6 +21,9 @@ public function load(array $configs, ContainerBuilder $container) $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); + + $container->getDefinition('biomejs.binary') + ->setArgument(2, $config['binary_version']); } public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface From a2c49acb53d2b358fd36f3e78998d6c6b4868276 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 09:33:39 +0200 Subject: [PATCH 3/8] imp(ci): add Windows and MacOS to test job --- .github/workflows/ci.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index daa623a..e16a465 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -59,6 +59,14 @@ jobs: - PHP_VERSION: '8.3' SYMFONY_VERSION: '7.0' + # Highest supported PHP version with the latest Symfony version, on Windows and macOS + - PHP_VERSION: '8.3' + SYMFONY_VERSION: '7.0' + OS: windows-latest + - PHP_VERSION: '8.3' + SYMFONY_VERSION: '7.0' + OS: macos-14 + # Latest 7.x development releases - PHP_VERSION: '8.1' SYMFONY_VERSION: '7.*' From 468e3902cf9007cdedf599b7694af9f3b21c4234 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 09:37:26 +0200 Subject: [PATCH 4/8] chore: fix PHPStan issues --- phpstan-baseline.neon | 90 ++++++++++++++++++++ phpstan.neon.dist | 7 +- src/DependencyInjection/BiomeJsExtension.php | 3 +- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 phpstan-baseline.neon diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..73ecef3 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,90 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\$apply of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$applyUnsafe of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$changed of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$formatterEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$linterEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$organizeImportsEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$path of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects array\\, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$since of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects string\\|null, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$staged of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:check\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCheckCommand.php + + - + message: "#^Parameter \\$changed of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Parameter \\$formatterEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Parameter \\$linterEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Parameter \\$organizeImportsEnabled of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects bool, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Parameter \\$path of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects array\\, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Parameter \\$since of method Kocal\\\\BiomeJsBundle\\\\BiomeJs\\:\\:ci\\(\\) expects string\\|null, mixed given\\.$#" + count: 1 + path: src/Command/BiomeJsCiCommand.php + + - + message: "#^Cannot call method end\\(\\) on Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\|null\\.$#" + count: 1 + path: src/DependencyInjection/BiomeJsExtension.php + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:end\\(\\)\\.$#" + count: 1 + path: src/DependencyInjection/BiomeJsExtension.php + + - + message: "#^Method Kocal\\\\BiomeJsBundle\\\\DependencyInjection\\\\BiomeJsExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/DependencyInjection/BiomeJsExtension.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c7a5610..3edc39b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,6 +1,11 @@ +includes: + - phpstan-baseline.neon + parameters: level: max paths: - src - - tests \ No newline at end of file + - tests + + reportUnmatchedIgnoredErrors: false \ No newline at end of file diff --git a/src/DependencyInjection/BiomeJsExtension.php b/src/DependencyInjection/BiomeJsExtension.php index bbbc736..269cc05 100644 --- a/src/DependencyInjection/BiomeJsExtension.php +++ b/src/DependencyInjection/BiomeJsExtension.php @@ -20,13 +20,14 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('services.php'); $configuration = $this->getConfiguration($configs, $container); + /** @var array $config */ $config = $this->processConfiguration($configuration, $configs); $container->getDefinition('biomejs.binary') ->setArgument(2, $config['binary_version']); } - public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface + public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface { return $this; } From 8fe14ff145726a79ead850bfb8b86c96d2868db5 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 09:46:30 +0200 Subject: [PATCH 5/8] chore(ci): fix matrix --- .github/workflows/ci.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e16a465..aa8309d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,11 +35,11 @@ jobs: fail-fast: false matrix: config: - # Minimum supported dependencies with the latest and oldest PHP version + # Minimum supported dependencies and minimum supported PHP version - PHP_VERSION: '8.1' COMPOSER_FLAGS: --prefer-stable --prefer-lowest SYMFONY_VERSION: '6.4' - - PHP_VERSION: '8.1' + - PHP_VERSION: '8.2' COMPOSER_FLAGS: --prefer-stable --prefer-lowest SYMFONY_VERSION: '7.0' @@ -52,8 +52,6 @@ jobs: SYMFONY_VERSION: '6.4' # Latest 7.0 stable releases - - PHP_VERSION: '8.1' - SYMFONY_VERSION: '7.0' - PHP_VERSION: '8.2' SYMFONY_VERSION: '7.0' - PHP_VERSION: '8.3' @@ -68,13 +66,10 @@ jobs: OS: macos-14 # Latest 7.x development releases - - PHP_VERSION: '8.1' - SYMFONY_VERSION: '7.*' - STABILITY: dev - PHP_VERSION: '8.2' SYMFONY_VERSION: '7.*' STABILITY: dev - - PHP_VERSION: '8.2' + - PHP_VERSION: '8.3' SYMFONY_VERSION: '7.*' STABILITY: dev env: From e3e223c3624c011da06e3d2f898b9e296de39310 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 18:35:25 +0200 Subject: [PATCH 6/8] chore: add tests --- config/services.php | 6 +++ phpunit.xml.dist | 1 + src/BiomeJs.php | 7 +-- tests/BiomeJsBinaryTest.php | 44 ++++++++++++++++++ tests/FunctionalTest.php | 67 ++++++++++++++++++++++++++++ tests/KocalBiomeJsBundleTest.php | 18 ++++++++ tests/MyTest.php | 15 ------- tests/fixtures/.gitignore | 1 + tests/fixtures/BiomeJsTestKernel.php | 58 ++++++++++++++++++++++++ tests/fixtures/bootstrap.js | 3 ++ tests/fixtures/say-hello.ts | 4 ++ 11 files changed, 206 insertions(+), 18 deletions(-) create mode 100644 tests/BiomeJsBinaryTest.php create mode 100644 tests/FunctionalTest.php create mode 100644 tests/KocalBiomeJsBundleTest.php delete mode 100644 tests/MyTest.php create mode 100644 tests/fixtures/.gitignore create mode 100644 tests/fixtures/BiomeJsTestKernel.php create mode 100644 tests/fixtures/bootstrap.js create mode 100644 tests/fixtures/say-hello.ts diff --git a/config/services.php b/config/services.php index f16c680..91ec6d1 100644 --- a/config/services.php +++ b/config/services.php @@ -10,6 +10,11 @@ use function Symfony\Component\DependencyInjection\Loader\Configurator\service; return static function (ContainerConfigurator $container): void { + $container->parameters() + // Internal parameter to enable/disable TTY mode, useful for tests (if someone has a better idea, feel free to suggest it!) + ->set('biomejs.use_tty', true) + ; + $container->services() ->set('biomejs.binary', BiomeJsBinary::class) ->args([ @@ -21,6 +26,7 @@ ->set('biomejs', BiomeJs::class) ->args([ service('biomejs.binary'), + param('biomejs.use_tty') ]) ->set('biomejs.command.ci', BiomeJsCiCommand::class) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4d58ef9..219f6f2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,7 @@ + diff --git a/src/BiomeJs.php b/src/BiomeJs.php index e0f08bf..8451481 100644 --- a/src/BiomeJs.php +++ b/src/BiomeJs.php @@ -12,7 +12,8 @@ final class BiomeJs private ?SymfonyStyle $output; public function __construct( - private readonly BiomeJsBinary $biomeJsBinary + private readonly BiomeJsBinary $biomeJsBinary, + private readonly bool $useTty = true, ) { } @@ -58,7 +59,7 @@ public function check( $this->biomeJsBinary->setOutput($this->output); $process = $this->biomeJsBinary->createProcess($arguments); - $process->setTty(true); + $process->setTty($this->useTty); $this->output?->note('Executing Biome.js "check" (pass -v to see more details).'); if ($this->output?->isVerbose()) { @@ -97,7 +98,7 @@ public function ci( $this->biomeJsBinary->setOutput($this->output); $process = $this->biomeJsBinary->createProcess($arguments); - $process->setTty(true); + $process->setTty($this->useTty); $this->output?->note('Executing Biome.js "ci" (pass -v to see more details).'); if ($this->output?->isVerbose()) { diff --git a/tests/BiomeJsBinaryTest.php b/tests/BiomeJsBinaryTest.php new file mode 100644 index 0000000..2558198 --- /dev/null +++ b/tests/BiomeJsBinaryTest.php @@ -0,0 +1,44 @@ +remove($binaryDownloadDir); + } + $fs->mkdir($binaryDownloadDir); + + $client = new MockHttpClient([ + new MockResponse('fake binary contents'), + ]); + + $binary = new BiomeJsBinary( + __DIR__, + $binaryDownloadDir, + 'fake-version', + $client + ); + $process = $binary->createProcess(['check', '--apply', '*.{js,ts}']); + $this->assertFileExists($binaryDownloadDir.'/fake-version/'.BiomeJsBinary::getBinaryName()); + + // Windows doesn't wrap arguments in quotes + $expectedTemplate = '\\' === \DIRECTORY_SEPARATOR ? '"%s" check --apply *.{js,ts}' : "'%s' 'check' '--apply' '*.{js,ts}'"; + + $this->assertSame( + sprintf($expectedTemplate, $binaryDownloadDir.'/fake-version/'.BiomeJsBinary::getBinaryName()), + $process->getCommandLine() + ); + } +} \ No newline at end of file diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php new file mode 100644 index 0000000..fa75942 --- /dev/null +++ b/tests/FunctionalTest.php @@ -0,0 +1,67 @@ +remove($biomejsVarDir); + } + } + + public function testCommandCheck(): void + { + self::bootKernel(); + $application = new Application(self::$kernel); + + $command = $application->find('biomejs:check'); + $commandTester = new CommandTester($command); + + $statusCode = $commandTester->execute([ + 'path' => [__DIR__.'/fixtures/'], + ]); + self::assertSame(Command::FAILURE, $statusCode, 'The command should return a non-zero exit code'); + + $output = $commandTester->getDisplay(true); + self::assertStringContainsString('Biome.js check failed: see output above.', $output); + // Linter + self::assertStringContainsString('BiomeJsBundle/tests/fixtures/say-hello.ts:3:17 lint/style/useTemplate', $output); + self::assertStringContainsString('Template literals are preferred over string concatenation.', $output); + // Formatter + self::assertStringContainsString('BiomeJsBundle/tests/fixtures/bootstrap.js format', $output); + self::assertStringContainsString('Formatter would have printed the following content:', $output); + } + + public function testCommandCi(): void + { + self::bootKernel(); + $application = new Application(self::$kernel); + + $command = $application->find('biomejs:ci'); + $commandTester = new CommandTester($command); + + $statusCode = $commandTester->execute([ + 'path' => [__DIR__.'/fixtures/'], + ]); + self::assertSame(Command::FAILURE, $statusCode, 'The command should return a non-zero exit code'); + + $output = $commandTester->getDisplay(true); + self::assertStringContainsString('Biome.js ci failed: see output above.', $output); + // Linter + self::assertStringContainsString('BiomeJsBundle/tests/fixtures/say-hello.ts:3:17 lint/style/useTemplate', $output); + self::assertStringContainsString('Template literals are preferred over string concatenation.', $output); + // Formatter + self::assertStringContainsString('BiomeJsBundle/tests/fixtures/bootstrap.js format', $output); + self::assertStringContainsString('File content differs from formatting output', $output); + } +} \ No newline at end of file diff --git a/tests/KocalBiomeJsBundleTest.php b/tests/KocalBiomeJsBundleTest.php new file mode 100644 index 0000000..7634bec --- /dev/null +++ b/tests/KocalBiomeJsBundleTest.php @@ -0,0 +1,18 @@ +getContainerExtension()); + } +} \ No newline at end of file diff --git a/tests/MyTest.php b/tests/MyTest.php deleted file mode 100644 index 953a61c..0000000 --- a/tests/MyTest.php +++ /dev/null @@ -1,15 +0,0 @@ -assertTrue(true); - } -} diff --git a/tests/fixtures/.gitignore b/tests/fixtures/.gitignore new file mode 100644 index 0000000..cef5ae5 --- /dev/null +++ b/tests/fixtures/.gitignore @@ -0,0 +1 @@ +var/ \ No newline at end of file diff --git a/tests/fixtures/BiomeJsTestKernel.php b/tests/fixtures/BiomeJsTestKernel.php new file mode 100644 index 0000000..71a3548 --- /dev/null +++ b/tests/fixtures/BiomeJsTestKernel.php @@ -0,0 +1,58 @@ +setParameter('biomejs.use_tty', false); + + $container->loadFromExtension('framework', [ + 'secret' => 'foo', + 'test' => true, + 'http_method_override' => true, + 'handle_all_throwables' => true, + 'php_errors' => [ + 'log' => true, + ], + ]); + } + + public function getCacheDir(): string + { + return sys_get_temp_dir().'/cache'.spl_object_hash($this); + } + + public function getLogDir(): string + { + return sys_get_temp_dir().'/logs'.spl_object_hash($this); + } + + public function getProjectDir(): string + { + return __DIR__; + } +} \ No newline at end of file diff --git a/tests/fixtures/bootstrap.js b/tests/fixtures/bootstrap.js new file mode 100644 index 0000000..c16eaa3 --- /dev/null +++ b/tests/fixtures/bootstrap.js @@ -0,0 +1,3 @@ +import { startStimulusApp} from '@symfony/stimulus-bundle'; + +const app=startStimulusApp(); \ No newline at end of file diff --git a/tests/fixtures/say-hello.ts b/tests/fixtures/say-hello.ts new file mode 100644 index 0000000..5307d1f --- /dev/null +++ b/tests/fixtures/say-hello.ts @@ -0,0 +1,4 @@ +export function sayHello(message : string) : void +{ + console.log('Hello, '+message+'!'); +} \ No newline at end of file From 07c30447e81a97251ea891f2f28b41e3561c7110 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 18:56:12 +0200 Subject: [PATCH 7/8] doc --- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97d4867..fd538fb 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,76 @@ # BiomeJsBundle +[![.github/workflows/ci.yaml](https://github.com/Kocal/BiomeJsBundle/actions/workflows/ci.yaml/badge.svg)](https://github.com/Kocal/BiomeJsBundle/actions/workflows/ci.yaml) + This bundle makes it easy to use [Biome.js](https://biomejs.dev/) in your Symfony project, to lint and format your assets files without Node.js (ex: when using Symfony's [AssetMapper Component](https://symfony.com/doc/current/frontend/asset_mapper.html)). ## Installation +Install the bundle with Composer: + ```bash -composer require kocal/biome-js-bundle +composer require kocal/biome-js-bundle --dev +``` + +The bundle should have been automatically enabled in your Symfony application (`config/bundles.php`). +If that's not the case, you can enable it manually: + +```php +// config/bundles.php +return [ + // ... + Kocal\BiomeJsBundle\KocalBiomeJsBundle::class => ['all' => true], +]; +``` + +## Configuration + +If you want to use a specific version of Biome.js, you can configure it in your `config/packages/kocal_biome_js.yaml`: + +```yaml +kocal_biome_js: + version: v1.7.3 +``` + +To [configure Biome.js it-self](https://biomejs.dev/reference/configuration), you must create a `biome.json` file at the root of your project. + +A recommended configuration for Symfony projects is to ignore files from `assets/vendor/`, `vendor/` and `public/bundles/`: +```json +{ + "files": { + "ignore": [ + "assets/vendor/*", + "vendor/*", + "public/bundles/*" + ] + } +} ``` ## Usage -TODO +The latest Biome.js CLI binary is automatically installed (if not already installed) when running one of the `biome:*` command. + +### `biome:check` + +Runs formatter, linter and import sorting to the requested files. +```bash +# Shows format and lint errors +php bin/console biome:check . + +# Shows format and lint errors, and fix them if possible +php bin/console biome:check . --apply +``` + +### `biome:ci` + +Command to use in CI environments. Runs formatter, linter and import sorting to the requested files. + +Files won't be modified, the command is a read-only operation. + +```bash +# Shows format and lint errors +php bin/console biome:ci . From a99552c4a9b4c321e87dfd5bad30d8b2903e2a44 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Fri, 17 May 2024 20:02:11 +0200 Subject: [PATCH 8/8] chore: qa --- tests/BiomeJsBinaryTest.php | 9 +++++---- tests/FunctionalTest.php | 11 +++++++---- tests/KocalBiomeJsBundleTest.php | 3 ++- tests/fixtures/BiomeJsTestKernel.php | 16 ++++++++-------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/tests/BiomeJsBinaryTest.php b/tests/BiomeJsBinaryTest.php index 2558198..051f5c8 100644 --- a/tests/BiomeJsBinaryTest.php +++ b/tests/BiomeJsBinaryTest.php @@ -1,4 +1,5 @@ remove($binaryDownloadDir); @@ -31,14 +32,14 @@ public function testBinaryIsDownloadedIfNotExists(): void $client ); $process = $binary->createProcess(['check', '--apply', '*.{js,ts}']); - $this->assertFileExists($binaryDownloadDir.'/fake-version/'.BiomeJsBinary::getBinaryName()); + $this->assertFileExists($binaryDownloadDir . '/fake-version/' . BiomeJsBinary::getBinaryName()); // Windows doesn't wrap arguments in quotes $expectedTemplate = '\\' === \DIRECTORY_SEPARATOR ? '"%s" check --apply *.{js,ts}' : "'%s' 'check' '--apply' '*.{js,ts}'"; $this->assertSame( - sprintf($expectedTemplate, $binaryDownloadDir.'/fake-version/'.BiomeJsBinary::getBinaryName()), + sprintf($expectedTemplate, $binaryDownloadDir . '/fake-version/' . BiomeJsBinary::getBinaryName()), $process->getCommandLine() ); } -} \ No newline at end of file +} diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index fa75942..0551822 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -1,4 +1,5 @@ remove($biomejsVarDir); } } @@ -22,13 +23,14 @@ protected function setUp(): void public function testCommandCheck(): void { self::bootKernel(); + self::assertNotNull(self::$kernel); $application = new Application(self::$kernel); $command = $application->find('biomejs:check'); $commandTester = new CommandTester($command); $statusCode = $commandTester->execute([ - 'path' => [__DIR__.'/fixtures/'], + 'path' => [__DIR__ . '/fixtures/'], ]); self::assertSame(Command::FAILURE, $statusCode, 'The command should return a non-zero exit code'); @@ -45,13 +47,14 @@ public function testCommandCheck(): void public function testCommandCi(): void { self::bootKernel(); + self::assertNotNull(self::$kernel); $application = new Application(self::$kernel); $command = $application->find('biomejs:ci'); $commandTester = new CommandTester($command); $statusCode = $commandTester->execute([ - 'path' => [__DIR__.'/fixtures/'], + 'path' => [__DIR__ . '/fixtures/'], ]); self::assertSame(Command::FAILURE, $statusCode, 'The command should return a non-zero exit code'); @@ -64,4 +67,4 @@ public function testCommandCi(): void self::assertStringContainsString('BiomeJsBundle/tests/fixtures/bootstrap.js format', $output); self::assertStringContainsString('File content differs from formatting output', $output); } -} \ No newline at end of file +} diff --git a/tests/KocalBiomeJsBundleTest.php b/tests/KocalBiomeJsBundleTest.php index 7634bec..b5ee51d 100644 --- a/tests/KocalBiomeJsBundleTest.php +++ b/tests/KocalBiomeJsBundleTest.php @@ -1,4 +1,5 @@ getContainerExtension()); } -} \ No newline at end of file +} diff --git a/tests/fixtures/BiomeJsTestKernel.php b/tests/fixtures/BiomeJsTestKernel.php index 71a3548..46656ff 100644 --- a/tests/fixtures/BiomeJsTestKernel.php +++ b/tests/fixtures/BiomeJsTestKernel.php @@ -1,4 +1,5 @@ setParameter('biomejs.use_tty', false); @@ -43,16 +43,16 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa public function getCacheDir(): string { - return sys_get_temp_dir().'/cache'.spl_object_hash($this); + return sys_get_temp_dir() . '/cache' . spl_object_hash($this); } public function getLogDir(): string { - return sys_get_temp_dir().'/logs'.spl_object_hash($this); + return sys_get_temp_dir() . '/logs' . spl_object_hash($this); } public function getProjectDir(): string { return __DIR__; } -} \ No newline at end of file +}