From 6653769ca889d831c756da6751f612824c28638c Mon Sep 17 00:00:00 2001
From: Jason McLaughlin <5099527+siemova@users.noreply.github.com>
Date: Wed, 16 Aug 2023 17:52:38 -0500
Subject: [PATCH] Support environment variable for patch output path
---
README.md | 13 +++--
src/Command/GenerateCommand.php | 5 +-
src/FileSystem/PathResolver.php | 10 ++++
src/PatchFileFactory.php | 24 ++++++++-
src/VendorDirProvider.php | 7 +--
.../PatchFileFactory/PatchFileFactoryTest.php | 50 +++++++++++++++++--
6 files changed, 94 insertions(+), 15 deletions(-)
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..4c85f085 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(
@@ -23,6 +27,24 @@ public function createPatchFilePath(OldAndNewFile $oldAndNewFile, string $vendor
$relativeFilePathWithoutSuffix = Strings::lower($inVendorRelativeFilePath);
$pathFileName = Strings::webalize($relativeFilePathWithoutSuffix) . '.patch';
- return 'patches' . DIRECTORY_SEPARATOR . $pathFileName;
+ return $this->getOutputPathRelativeToProjectRoot() . DIRECTORY_SEPARATOR . $pathFileName;
+ }
+
+ 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 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;
}
}