diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a6fd7..1d06c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -1.6.0 +2.0.0 ===== +* (bc) Remove deprecated init commands +* (feature) Automatically detect the package type according to the type in `composer.json`. +* (feature) Write back the package type, if none was set. * (improvement) Add option to not automatically run `composer update` after Janus finished. * (feature) Add composer plugin to run composer automatically. diff --git a/LICENSE b/LICENSE index 59466da..3b72341 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 21TORR +Copyright (c) 2025 21TORR Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Command/InitializeCommand.php b/src/Command/InitializeCommand.php index 95abb7c..caf0973 100644 --- a/src/Command/InitializeCommand.php +++ b/src/Command/InitializeCommand.php @@ -2,8 +2,12 @@ namespace Janus\Command; -use Janus\Initializer\LibraryInitializer; -use Janus\Initializer\SymfonyInitializer; +use Janus\Composer\ComposerJson; +use Janus\Exception\InvalidCallException; +use Janus\Exception\JanusException; +use Janus\Package\PackageInitializer; +use Janus\Package\PackageManager; +use Janus\Package\PackageType; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -17,10 +21,6 @@ final class InitializeCommand extends Command "symfony", "library", ]; - private const array LEGACY_COMMANDS = [ - "init-symfony", - "init-library", - ]; /** */ @@ -36,13 +36,12 @@ protected function configure () : void { $this ->setDescription("Initializes a given command") - ->setAliases(self::LEGACY_COMMANDS) ->addArgument( "type", InputArgument::OPTIONAL, "The project type to initialize", default: null, - suggestedValues: self::ALLOWED_TYPES, + suggestedValues: PackageType::values(), ) ->addOption( "no-auto-install", @@ -57,48 +56,115 @@ protected function configure () : void protected function execute (InputInterface $input, OutputInterface $output) : int { $io = new TorrStyle($input, $output); + $projectHelper = new PackageManager($io); + $packageInitializer = new PackageInitializer(); + $io->title("Janus: Initialize"); + $runComposerAutomatically = !$input->getOption('no-auto-install'); - if (\in_array($input->getFirstArgument(), self::LEGACY_COMMANDS, true)) + try { - $io->caution("You are using a deprecated command. Use the `init` command instead."); - } + $composerJson = $projectHelper->loadComposerJson(); + $packageType = $this->fetchPackageType($io, $composerJson, $input->getArgument("type")); - $type = $input->getArgument("type"); - \assert(null === $type || \is_string($type)); + $io->writeln(\sprintf( + "• Initializing janus for type %s", + $packageType->value, + )); - if (!\in_array($type, self::ALLOWED_TYPES, true)) - { - if (null !== $type) + $io->writeln("• Copying main init files"); + $projectHelper->copyInitFilesIntoProject($packageType); + + $io->writeln("• Updating composer.json"); + + // write basics + match ($packageType) + { + PackageType::Symfony => $packageInitializer->initializeSymfony($composerJson), + PackageType::Library => $packageInitializer->initializeLibrary($composerJson), + }; + + // set project type (only if it is not yet set. We want to keep even unknown values here, so only set it if it is unset) + if (!$composerJson->hasType()) + { + $io->writeln("• Your composer.json has no type set"); + $io->writeln(\sprintf( + "• Setting it to the type %s (according to your selection %s)", + $packageType->getComposerType(), + $packageType->value, + )); + + $composerJson->replaceConfig([ + "type" => $packageType->getComposerType(), + ]); + } + + $projectHelper->writeComposerJson($composerJson); + + if ($runComposerAutomatically) + { + $io->writeln("• Running composer update..."); + $projectHelper->runComposerInProject(["update"]); + } + else { - $io->error("Used invalid type: {$type}"); + $io->caution("Your project was updated, you should run `composer update`."); } - $type = $io->choice("Please select the type to initialize", self::ALLOWED_TYPES); + return self::SUCCESS; } + catch (JanusException $exception) + { + $io->error($exception->getMessage()); - \assert(\is_string($type)); + return self::FAILURE; + } + } - $io->comment(\sprintf( - "Initializing janus for type %s", - $type, - )); + /** + * + */ + private function fetchPackageType ( + TorrStyle $io, + ComposerJson $composerJson, + mixed $typeCliArgument, + ) : PackageType + { + // first try CLI parameter + $packageType = \is_string($typeCliArgument) + ? PackageType::tryFrom($typeCliArgument) + : null; - $runComposerAutomatically = !$input->getOption("no-auto-install"); + if (null !== $packageType) + { + return $packageType; + } - try + // then error out if the user explicitly passed an invalid value + if (null !== $typeCliArgument) { - return match ($type) - { - "symfony" => (new SymfonyInitializer())->initialize($io, $runComposerAutomatically), - "library" => (new LibraryInitializer())->initialize($io, $runComposerAutomatically), - }; + throw new InvalidCallException(\sprintf( + "Invalid type selected: %s", + \is_scalar($typeCliArgument) + ? $typeCliArgument + : get_debug_type($typeCliArgument), + )); } - catch (\Throwable $exception) + + // no CLI parameter passed, so test if we can detect the type from composer.json + $packageType = $composerJson->getType(); + + if (null !== $packageType) { - $io->error("Running janus failed: {$exception->getMessage()}"); + $io->writeln("• Automatically detected type from the package type in your composer.json"); - return 2; + return $packageType; } + + // could not detect, so just ask for it + $type = $io->choice("Please select the type to initialize", PackageType::values()); + \assert(\is_string($type)); + + return PackageType::from($type); } } diff --git a/src/Composer/ComposerJson.php b/src/Composer/ComposerJson.php new file mode 100644 index 0000000..24f76eb --- /dev/null +++ b/src/Composer/ComposerJson.php @@ -0,0 +1,102 @@ + */ + public private(set) array $content, + ) {} + + /** + * Takes a list of scripts to replace and updates the configs. + * + * The $scripts array has a keywords as key, and replaces the line containing that keyword. + * So for example the key "phpunit" would replace the line that contains "phpunit". + * If there are multiple lines matching, all will be replaced. + * If there are no lines matching, the call will just be appended. + * + * @param string $key the scripts key to update + * @param array $scripts the scripts to replace + */ + public function updateScripts (string $key, array $scripts) : void + { + $allExistingScripts = $this->content["scripts"] ?? []; + + if (!\is_array($allExistingScripts)) + { + throw new InvalidSetupException(\sprintf( + "Invalid composer.json: scripts must be an array, %s given", + get_debug_type($allExistingScripts), + )); + } + + $existingScripts = $allExistingScripts[$key] ?? []; + // keep existing scripts + $result = []; + \assert(\is_array($existingScripts)); + + foreach ($existingScripts as $line) + { + \assert(\is_string($line)); + + foreach ($scripts as $replacedKeyword => $newLine) + { + if (str_contains($line, $replacedKeyword)) + { + continue 2; + } + } + + // append the line if no replacement matches + $result[] = $line; + } + + // append all new lines + foreach ($scripts as $newLine) + { + $result[] = $newLine; + } + + $allExistingScripts[$key] = $result; + $this->content["scripts"] = $allExistingScripts; + } + + /** + * Add the given config to the projects composer.json + * + * @param array $config + */ + public function replaceConfig (array $config) : void + { + $this->content = array_replace_recursive( + $this->content, + $config, + ); + } + + /** + * + */ + public function getType () : ?PackageType + { + return PackageType::tryFromComposerType($this->content["type"] ?? null); + } + + /** + * + */ + public function hasType () : bool + { + return \array_key_exists("type", $this->content); + } +} diff --git a/src/Composer/JanusPlugin.php b/src/Composer/JanusPlugin.php index 6ba8cf8..f79d0ce 100644 --- a/src/Composer/JanusPlugin.php +++ b/src/Composer/JanusPlugin.php @@ -14,6 +14,7 @@ use Composer\Script\Event; use Composer\Script\ScriptEvents; use Janus\Command\InitializeCommand; +use Janus\Package\PackageType; use Symfony\Component\Process\Process; /** @@ -95,17 +96,32 @@ public function afterAutoloadDump (Event $event) : void $io = $event->getIO(); $io->write("\nJanus update detected, running janus update\n"); - $selected = $io->select( - "What are you currently using?", - InitializeCommand::ALLOWED_TYPES, - "library", + // please note, that the detection can fail: composer defaults to "library", if it's not set + $packageType = PackageType::tryFromComposerType( + $event->getComposer()->getPackage()->getType(), ); - $type = InitializeCommand::ALLOWED_TYPES[$selected] ?? null; + + if (null === $packageType) + { + $selected = $io->select( + "What are you currently using?", + InitializeCommand::ALLOWED_TYPES, + "library", + ); + $packageType = PackageType::tryFromComposerType(InitializeCommand::ALLOWED_TYPES[$selected] ?? null); + } + else + { + $io->write(\sprintf( + "Detected package type %s", + $packageType->value, + )); + } $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir'); \assert(\is_string($vendorDir)); - $success = $this->runJanus($io, $vendorDir, $type); + $success = $this->runJanus($io, $vendorDir, $packageType); if ($success) { @@ -123,17 +139,17 @@ public function afterAutoloadDump (Event $event) : void private function runJanus ( IOInterface $io, string $vendorDir, - ?string $type, + ?PackageType $type, ) : bool { $command = [ - "{$vendorDir}/bin/janus", + $this->findJanusExecutable($vendorDir), "init", ]; if (null !== $type) { - $command[] = $type; + $command[] = $type->value; } $command[] = "--no-auto-install"; @@ -150,11 +166,26 @@ static function ($type, $buffer) use ($io) : void return $output->isSuccessful(); } + /** + * Finds the path to the janus executable + */ + private function findJanusExecutable (string $vendorDir) : string + { + // first check if it's installed in the project via composer + if (is_dir("{$vendorDir}/bin/janus")) + { + return "{$vendorDir}/bin/janus"; + } + + // otherwise just fetch the executable from the library and run it + return \dirname(__DIR__, 2) . "/bin/janus"; + } + /** * @inheritDoc */ #[\Override] - public static function getSubscribedEvents () + public static function getSubscribedEvents () : array { return [ PackageEvents::POST_PACKAGE_INSTALL => "checkForJanusOperations", diff --git a/src/Exception/ComposerNotFoundException.php b/src/Exception/ComposerNotFoundException.php new file mode 100644 index 0000000..36f24da --- /dev/null +++ b/src/Exception/ComposerNotFoundException.php @@ -0,0 +1,10 @@ +cwd = (string) getcwd(); - } - - /** - * Copies the files from the given init dir to the project dir - */ - public function copyFilesIntoProject (string $directory) : void - { - $sourceDir = self::INIT_DIR . "/{$directory}/."; - - $this->runProcessInProject([ - "cp", - "-a", - $sourceDir, - ".", - ]); - } - - /** - * Add the given config to the projects composer.json - * - * @param array $config - */ - public function addToProjectComposerJson (array $config) : void - { - $jsonContent = array_replace_recursive( - $this->readProjectComposerJson(), - $config, - ); - - $this->writeProjectComposerJson($jsonContent); - } - - /** - * Takes a list of scripts to replace and updates the configs. - * - * The $scripts array has a keywords as key, and replaces the line containing that keyword. - * So for example the key "phpunit" would replace the line that contains "phpunit". - * If there are multiple lines matching, all will be replaced. - * If there are no lines matching, the call will just be appended. - * - * @param string $key the scripts key to update - * @param array $scripts the scripts to replace - */ - public function updateProjectComposerJsonScripts (string $key, array $scripts) : void - { - $jsonContent = $this->readProjectComposerJson(); - \assert(!isset($jsonContent["scripts"]) || \is_array($jsonContent["scripts"])); - - $existingScripts = $jsonContent["scripts"][$key] ?? []; - // keep existing scripts - $result = []; - - foreach ($existingScripts as $line) - { - foreach ($scripts as $replacedKeyword => $newLine) - { - if (str_contains($line, $replacedKeyword)) - { - continue 2; - } - } - - // append the line if no replacement matches - $result[] = $line; - } - - // append all new lines - foreach ($scripts as $newLine) - { - $result[] = $newLine; - } - - $jsonContent["scripts"][$key] = $result; - $this->writeProjectComposerJson($jsonContent); - } - - /** - * Writes the given config to the project composer.json - * - * @param array $config - */ - private function writeProjectComposerJson (array $config) : void - { - $filePath = "{$this->cwd}/composer.json"; - - file_put_contents( - $filePath, - json_encode( - $config, - \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_THROW_ON_ERROR, - ), - ); - } - - /** - * @return array - */ - private function readProjectComposerJson () : array - { - $filePath = "{$this->cwd}/composer.json"; - - $result = json_decode( - (string) file_get_contents($filePath), - true, - flags: \JSON_THROW_ON_ERROR, - ); - \assert(\is_array($result)); - - return $result; - } - - /** - * Runs a composer command in the project - * - * @param string[] $cmd - */ - public function runComposerInProject (array $cmd) : void - { - $finder = new ExecutableFinder(); - $composer = $finder->find("composer"); - - if (null === $composer) - { - throw new InvalidSetupException("Could not find locally installed composer"); - } - - array_unshift($cmd, $composer); - $cmd[] = "--ansi"; - $this->runProcessInProject($cmd); - } - - /** - * Runs the given command in the project directory - * - * @param string[] $cmd - */ - public function runProcessInProject (array $cmd) : void - { - $this->io->writeln(\sprintf( - "$> Running command %s", - implode(" ", $cmd), - )); - - $process = new Process( - $cmd, - cwd: $this->cwd, - ); - $process->mustRun(); - - $output = trim(\sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput())); - - if ("" !== $output) - { - $this->io->block( - trim(\sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput())), - prefix: " │ ", - ); - } - } -} diff --git a/src/Initializer/LibraryInitializer.php b/src/Initializer/LibraryInitializer.php deleted file mode 100644 index d4ef3c7..0000000 --- a/src/Initializer/LibraryInitializer.php +++ /dev/null @@ -1,62 +0,0 @@ -writeln("• Copying config files to the project..."); - $helper->copyFilesIntoProject("library"); - - $io->writeln("• Updating composer.json..."); - $helper->addToProjectComposerJson([ - "config" => [ - "allow-plugins" => [ - "bamarni/composer-bin-plugin" => true, - ], - "sort-packages" => true, - ], - "extra" => [ - "bamarni-bin" => [ - "bin-links" => false, - "forward-command" => true, - ], - ], - "require-dev" => [ - "bamarni/composer-bin-plugin" => "^1.8.2", - "roave/security-advisories" => "dev-latest", - ], - ]); - $helper->updateProjectComposerJsonScripts("fix-lint", [ - "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi", - "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", - ]); - $helper->updateProjectComposerJsonScripts("lint", [ - "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi", - "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer check --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", - ]); - $helper->updateProjectComposerJsonScripts("test", [ - "phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi -v", - ]); - - if ($runComposerAutomatically) - { - $io->writeln("• Running composer update..."); - $helper->runComposerInProject(["update"]); - } - else - { - $io->caution("Your project was updated, you should run `composer update`."); - } - - return 0; - } -} diff --git a/src/Initializer/SymfonyInitializer.php b/src/Initializer/SymfonyInitializer.php deleted file mode 100644 index 8b48c86..0000000 --- a/src/Initializer/SymfonyInitializer.php +++ /dev/null @@ -1,64 +0,0 @@ -writeln("• Copying config files to the project..."); - $helper->copyFilesIntoProject("symfony"); - - $io->writeln("• Updating composer.json..."); - $helper->addToProjectComposerJson([ - "config" => [ - "allow-plugins" => [ - "bamarni/composer-bin-plugin" => true, - ], - "sort-packages" => true, - ], - "extra" => [ - "bamarni-bin" => [ - "bin-links" => false, - "forward-command" => true, - ], - ], - "require-dev" => [ - "bamarni/composer-bin-plugin" => "^1.8.2", - "roave/security-advisories" => "dev-latest", - ], - ]); - $helper->updateProjectComposerJsonScripts("fix-lint", [ - "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi", - "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", - ]); - $helper->updateProjectComposerJsonScripts("lint", [ - "lint:yaml" => "bin/console lint:yaml config --parse-tags", - "lint:twig" => "bin/console lint:twig templates", - "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi", - "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer check --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", - ]); - $helper->updateProjectComposerJsonScripts("test", [ - "phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi -v", - ]); - - if ($runComposerAutomatically) - { - $io->writeln("• Running composer update..."); - $helper->runComposerInProject(["update"]); - } - else - { - $io->caution("Your project was updated, you should run `composer update`."); - } - - return 0; - } -} diff --git a/src/Package/PackageInitializer.php b/src/Package/PackageInitializer.php new file mode 100644 index 0000000..42dad95 --- /dev/null +++ b/src/Package/PackageInitializer.php @@ -0,0 +1,92 @@ +replaceConfig([ + "config" => [ + "allow-plugins" => [ + "21torr/janus" => true, + "bamarni/composer-bin-plugin" => true, + ], + "sort-packages" => true, + ], + "extra" => [ + "bamarni-bin" => [ + "bin-links" => false, + "forward-command" => true, + ], + ], + "require-dev" => [ + "bamarni/composer-bin-plugin" => "^1.8.2", + "roave/security-advisories" => "dev-latest", + ], + ]); + + $composerJson->updateScripts("fix-lint", [ + "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi", + "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", + ]); + + $composerJson->updateScripts("lint", [ + "lint:yaml" => "bin/console lint:yaml config --parse-tags", + "lint:twig" => "bin/console lint:twig templates", + "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi", + "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer check --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", + ]); + + $composerJson->updateScripts("test", [ + "phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi -v", + ]); + } + + /** + * + */ + public function initializeLibrary (ComposerJson $composerJson) : void + { + $composerJson->replaceConfig([ + "config" => [ + "allow-plugins" => [ + "bamarni/composer-bin-plugin" => true, + ], + "sort-packages" => true, + ], + "extra" => [ + "bamarni-bin" => [ + "bin-links" => false, + "forward-command" => true, + ], + ], + "require-dev" => [ + "bamarni/composer-bin-plugin" => "^1.8.2", + "roave/security-advisories" => "dev-latest", + ], + ]); + + $composerJson->updateScripts("fix-lint", [ + "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --ansi", + "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer fix --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", + ]); + + $composerJson->updateScripts("lint", [ + "normalize" => "@composer bin c-norm normalize \"$(pwd)/composer.json\" --indent-style tab --indent-size 1 --dry-run --ansi", + "cs-fixer" => "PHP_CS_FIXER_IGNORE_ENV=1 vendor-bin/cs-fixer/vendor/bin/php-cs-fixer check --diff --config vendor-bin/cs-fixer/vendor/21torr/php-cs-fixer/.php-cs-fixer.dist.php --no-interaction --ansi", + ]); + + $composerJson->updateScripts("test", [ + "phpstan" => "vendor-bin/phpstan/vendor/bin/phpstan analyze -c phpstan.neon . --ansi -v", + ]); + } +} diff --git a/src/Package/PackageManager.php b/src/Package/PackageManager.php new file mode 100644 index 0000000..fce92ff --- /dev/null +++ b/src/Package/PackageManager.php @@ -0,0 +1,157 @@ +initDir = \dirname(__DIR__, 2) . "/_init"; + $this->cwd = (string) getcwd(); + } + + /** + * Copies the files from the given init dir to the project dir + */ + public function copyInitFilesIntoProject (PackageType $packageType) : void + { + $sourceDir = "{$this->initDir}/{$packageType->value}/."; + + $this->runProcessInProject([ + "cp", + "-a", + $sourceDir, + ".", + ]); + } + + /** + */ + public function loadComposerJson () : ComposerJson + { + $filePath = "{$this->cwd}/composer.json"; + + if (!is_file($filePath) || !is_readable($filePath)) + { + throw new ComposerNotFoundException(\sprintf( + "composer.json not found at '%s'", + $filePath, + )); + } + + try + { + $result = json_decode( + (string) file_get_contents($filePath), + true, + depth: 512, + flags: \JSON_THROW_ON_ERROR, + ); + \assert(\is_array($result)); + + return new ComposerJson($result); + } + catch (\JsonException $exception) + { + throw new ComposerNotFoundException( + \sprintf( + "composer.json at '%s' contains invalid JSON", + $filePath, + ), + previous: $exception, + ); + } + } + + /** + * Writes the given config to the project composer.json + */ + public function writeComposerJson (ComposerJson $composerJson) : void + { + $filePath = "{$this->cwd}/composer.json"; + + try + { + file_put_contents( + $filePath, + json_encode( + $composerJson->content, + \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_THROW_ON_ERROR, + ), + ); + } + catch (\JsonException $exception) + { + throw new InvalidSetupException( + \sprintf( + "Could not write back composer.json at '%s'", + $filePath, + ), + previous: $exception, + ); + } + } + + /** + * Runs a composer command in the project + * + * @param string[] $cmd + */ + public function runComposerInProject (array $cmd) : void + { + $finder = new ExecutableFinder(); + $composer = $finder->find("composer"); + + if (null === $composer) + { + throw new InvalidSetupException("Could not find locally installed composer"); + } + + array_unshift($cmd, $composer); + $cmd[] = "--ansi"; + $this->runProcessInProject($cmd); + } + + /** + * Runs the given command in the project directory + * + * @param string[] $cmd + */ + public function runProcessInProject (array $cmd) : void + { + $this->io?->writeln(\sprintf( + "$> Running command %s", + implode(" ", $cmd), + )); + + $process = new Process( + $cmd, + cwd: $this->cwd, + ); + $process->mustRun(); + + $output = trim(\sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput())); + + if ("" !== $output) + { + $this->io?->block( + trim(\sprintf("%s\n%s", $process->getErrorOutput(), $process->getOutput())), + prefix: " │ ", + ); + } + } +} diff --git a/src/Package/PackageType.php b/src/Package/PackageType.php new file mode 100644 index 0000000..85c7d0e --- /dev/null +++ b/src/Package/PackageType.php @@ -0,0 +1,48 @@ + "project", + self::Library => "library", + }; + } + + /** + * @return list + */ + public static function values () : array + { + return array_map( + static fn (self $type) => $type->value, + self::cases(), + ); + } + + /** + * + */ + public static function tryFromComposerType (mixed $type) : ?self + { + return match ($type) + { + "project" => self::Symfony, + + "symfony-bundle", + "library" => self::Library, + + default => null, + }; + } +} diff --git a/vendor-bin/phpstan/composer.json b/vendor-bin/phpstan/composer.json index cf165ff..4eb9387 100644 --- a/vendor-bin/phpstan/composer.json +++ b/vendor-bin/phpstan/composer.json @@ -3,14 +3,14 @@ "php": "^8.3" }, "require-dev": { - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-deprecation-rules": "^1.2", - "phpstan/phpstan-doctrine": "^1.4", - "phpstan/phpstan-phpunit": "^1.4", - "phpstan/phpstan-symfony": "^1.4", + "phpstan/extension-installer": "^1.4.2", + "phpstan/phpstan": "^2.1.11", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-doctrine": "^2.0.2", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-symfony": "^2.0.4", "roave/security-advisories": "dev-latest", - "staabm/phpstan-todo-by": "^0.1.25" + "staabm/phpstan-todo-by": "^0.2" }, "config": { "sort-packages": true,