diff --git a/README.md b/README.md index 0ef99272..9e54ae65 100644 --- a/README.md +++ b/README.md @@ -42,17 +42,22 @@ Only `*.php` file is loaded, not the `*.php.old` one. This way you can **be sure vendor/bin/vendor-patches generate ``` -This tool will generate **patch files for all files created this** way in `/patches` directory: +This tool will generate **patch files for all vendor files modified this way**. + +By default, they will be created in the `patches` subdirectory of your repository, +but you can override this using the environment variable `VENDOR_PATCHES_OUTPUT_PATH`. +If its value is an absolute path, it must describe a path within the repository. +If a relative path, it will be relative to the repository root. ```bash -/patches/nette-di-di-extensions-injectextension.php.patch +patches/nette-di-di-extensions-injectextension.php.patch ``` -The patch path is based on original file path, so **the patch name is always unique**. +Each patch file name is based on the original file path, so **it is always unique**.
-Also, it will add configuration for `cweagans/composer-patches` to your `composer.json`: +Also, `generate` will add configuration for `cweagans/composer-patches` to your `composer.json`: ```json { diff --git a/src/Command/GenerateCommand.php b/src/Command/GenerateCommand.php index 6ce2739f..5abf69c3 100644 --- a/src/Command/GenerateCommand.php +++ b/src/Command/GenerateCommand.php @@ -12,6 +12,7 @@ use Symplify\VendorPatches\Composer\ComposerPatchesConfigurationUpdater; use Symplify\VendorPatches\Console\GenerateCommandReporter; use Symplify\VendorPatches\Differ\PatchDiffer; +use Symplify\VendorPatches\FileSystem\PathResolver; use Symplify\VendorPatches\Finder\OldToNewFilesFinder; use Symplify\VendorPatches\PatchFileFactory; use Symplify\VendorPatches\VendorDirProvider; @@ -77,7 +78,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($composerExtraPatches !== []) { $this->composerPatchesConfigurationUpdater->updateComposerJsonAndPrint( - getcwd() . '/composer.json', + PathResolver::getProjectRootPath() . 'composer.json', $composerExtraPatches ); } @@ -94,7 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function resolveProjectVendorDirectory(): string { - $projectVendorDirectory = getcwd() . '/vendor'; + $projectVendorDirectory = PathResolver::getProjectRootPath() . 'vendor'; if (file_exists($projectVendorDirectory)) { return $projectVendorDirectory; } diff --git a/src/FileSystem/PathResolver.php b/src/FileSystem/PathResolver.php index 4a202f19..caf108ce 100644 --- a/src/FileSystem/PathResolver.php +++ b/src/FileSystem/PathResolver.php @@ -16,6 +16,16 @@ final class PathResolver */ private const VENDOR_PACKAGE_DIRECTORY_REGEX = '#^(?.*?vendor\/(\w|\.|\-)+\/(\w|\.|\-)+)\/#si'; + public static function getAbsoluteRootPath(): string + { + return getenv('SystemDrive', true) . DIRECTORY_SEPARATOR; + } + + public static function getProjectRootPath(): string + { + return getcwd() . DIRECTORY_SEPARATOR; + } + public static function resolveVendorDirectory(string $filePath): string { $match = Strings::match($filePath, self::VENDOR_PACKAGE_DIRECTORY_REGEX); diff --git a/src/PatchFileFactory.php b/src/PatchFileFactory.php index 301c80c4..9193857c 100644 --- a/src/PatchFileFactory.php +++ b/src/PatchFileFactory.php @@ -13,6 +13,10 @@ */ final class PatchFileFactory { + public const DEFAULT_OUTPUT_PATH = 'patches'; + + public const OUTPUT_PATH_ENV_VAR = 'VENDOR_PATCHES_OUTPUT_PATH'; + public function createPatchFilePath(OldAndNewFile $oldAndNewFile, string $vendorDirectory): string { $inVendorRelativeFilePath = PathResolver::getRelativeFilePathFromDirectory( @@ -21,8 +25,26 @@ public function createPatchFilePath(OldAndNewFile $oldAndNewFile, string $vendor ); $relativeFilePathWithoutSuffix = Strings::lower($inVendorRelativeFilePath); - $pathFileName = Strings::webalize($relativeFilePathWithoutSuffix) . '.patch'; + $patchFileName = Strings::webalize($relativeFilePathWithoutSuffix) . '.patch'; + + return $this->getOutputPathRelativeToProjectRoot() . DIRECTORY_SEPARATOR . $patchFileName; + } + + private function getOutputPathRelativeToProjectRoot(): string + { + $outputPath = getenv(self::OUTPUT_PATH_ENV_VAR); + + if ($outputPath) { + if (!str_starts_with($outputPath, PathResolver::getAbsoluteRootPath())) { + return $outputPath; + } + + $projectRootPath = PathResolver::getProjectRootPath(); + if (str_starts_with($outputPath, $projectRootPath)) { + return PathResolver::getRelativeFilePathFromDirectory($outputPath, $projectRootPath); + } + } - return 'patches' . DIRECTORY_SEPARATOR . $pathFileName; + return self::DEFAULT_OUTPUT_PATH; } } diff --git a/src/VendorDirProvider.php b/src/VendorDirProvider.php index 54d2beb6..1fa81355 100644 --- a/src/VendorDirProvider.php +++ b/src/VendorDirProvider.php @@ -6,20 +6,21 @@ use Composer\Autoload\ClassLoader; use ReflectionClass; +use Symplify\VendorPatches\FileSystem\PathResolver; use Webmozart\Assert\Assert; final class VendorDirProvider { public static function provide(): string { - $rootFolder = getenv('SystemDrive', true) . DIRECTORY_SEPARATOR; + $absoluteRootPath = PathResolver::getAbsoluteRootPath(); $path = __DIR__; - while (! \str_ends_with($path, 'vendor') && $path !== $rootFolder) { + while (! \str_ends_with($path, 'vendor') && $path !== $absoluteRootPath) { $path = dirname($path); } - if ($path !== $rootFolder) { + if ($path !== $absoluteRootPath) { return $path; } diff --git a/tests/PatchFileFactory/PatchFileFactoryTest.php b/tests/PatchFileFactory/PatchFileFactoryTest.php index e4e0a692..5bd8de9d 100644 --- a/tests/PatchFileFactory/PatchFileFactoryTest.php +++ b/tests/PatchFileFactory/PatchFileFactoryTest.php @@ -10,17 +10,57 @@ final class PatchFileFactoryTest extends AbstractTestCase { - public function test(): void + private const FIXTURE_PATH = __DIR__ . DIRECTORY_SEPARATOR . 'Fixture'; + + private const NESTED_OUTPUT_PATH = 'path' . DIRECTORY_SEPARATOR . 'to' . DIRECTORY_SEPARATOR . 'patches'; + + public function testDefaultOutputPath(): void + { + $patchFilePath = $this->makePatchFilePath(); + $expectedPath = PatchFileFactory::DEFAULT_OUTPUT_PATH . DIRECTORY_SEPARATOR . 'some-new-file-php.patch'; + + $this->assertSame($expectedPath, $patchFilePath); + } + + public function testRelativeEnvironmentOutputPath(): void + { + $relativeOutputPath = self::NESTED_OUTPUT_PATH; + $patchFilePath = $this->makePatchFilePathWithEnvironmentOutputPath($relativeOutputPath); + $expectedPath = self::NESTED_OUTPUT_PATH . DIRECTORY_SEPARATOR . 'some-new-file-php.patch'; + + $this->assertSame($expectedPath, $patchFilePath); + } + + public function testAbsoluteEnvironmentOutputPath(): void + { + $absoluteOutputPath = dirname(__FILE__, 3) . DIRECTORY_SEPARATOR . self::NESTED_OUTPUT_PATH; + $patchFilePath = $this->makePatchFilePathWithEnvironmentOutputPath($absoluteOutputPath); + $expectedPath = self::NESTED_OUTPUT_PATH . DIRECTORY_SEPARATOR . 'some-new-file-php.patch'; + + $this->assertSame($expectedPath, $patchFilePath); + } + + private function makePatchFilePath(): string { $patchFileFactory = $this->make(PatchFileFactory::class); $oldAndNewFile = new OldAndNewFile( - __DIR__ . '/Fixture/some_old_file.php', - __DIR__ . '/Fixture/some_new_file.php', + self::FIXTURE_PATH . DIRECTORY_SEPARATOR . 'some_old_file.php', + self::FIXTURE_PATH . DIRECTORY_SEPARATOR . 'some_new_file.php', 'package/name' ); - $pathFilePath = $patchFileFactory->createPatchFilePath($oldAndNewFile, __DIR__ . '/Fixture'); - $this->assertSame('patches/some-new-file-php.patch', $pathFilePath); + return $patchFileFactory->createPatchFilePath($oldAndNewFile, self::FIXTURE_PATH); + } + + private function makePatchFilePathWithEnvironmentOutputPath(string $environmentOutputPath): string + { + putenv(PatchFileFactory::OUTPUT_PATH_ENV_VAR . '=' . $environmentOutputPath); + + $patchFilePath = $this->makePatchFilePath(); + + putenv(PatchFileFactory::OUTPUT_PATH_ENV_VAR); // Unset + + return $patchFilePath; } }