mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 20:53:31 +00:00
[Prefixed Rector] Add package-scoper approach (#4559)
This commit is contained in:
parent
4e06c46253
commit
d4883d44d9
38
.github/workflows/build_scoped_rector.yaml
vendored
Normal file
38
.github/workflows/build_scoped_rector.yaml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
# builds the content of https://github.com/rectorphp/rector-prefixed
|
||||
name: Build Scoped Rector
|
||||
|
||||
on:
|
||||
push:
|
||||
# see https://github.community/t/how-to-run-github-actions-workflow-only-for-new-tags/16075/10?u=tomasvotruba
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build_scoped_rector:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v2
|
||||
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
# fixes https://github.com/rectorphp/rector/pull/4559/checks?check_run_id=1359814403, see https://github.com/shivammathur/setup-php#composer-github-oauth
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
|
||||
# 1. prepare dependencies
|
||||
- run: sh build-rector-scoped.sh
|
||||
|
||||
# 2. publish it to remote repository
|
||||
-
|
||||
uses: symplify/github-action-monorepo-split@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
with:
|
||||
package-directory: 'rector-scoped'
|
||||
split-repository-organization: 'rectorphp'
|
||||
split-repository-name: 'rector-prefixed'
|
||||
user-name: "kaizen-ci"
|
||||
user-email: "info@kaizen-ci.org"
|
48
.github/workflows/build_scoped_rector_tagged.yaml
vendored
Normal file
48
.github/workflows/build_scoped_rector_tagged.yaml
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
# builds the content of https://github.com/rectorphp/rector-prefixed
|
||||
name: Build Scoped Rector Tagged
|
||||
|
||||
on:
|
||||
push:
|
||||
# see https://github.community/t/how-to-run-github-actions-workflow-only-for-new-tags/16075/10?u=tomasvotruba
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build_scoped_rector_tagged:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
uses: actions/checkout@v2
|
||||
# this is required for "WyriHaximus/github-action-get-previous-tag" workflow
|
||||
# see https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
# fixes https://github.com/rectorphp/rector/pull/4559/checks?check_run_id=1359814403, see https://github.com/shivammathur/setup-php#composer-github-oauth
|
||||
env:
|
||||
COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
|
||||
# 1. prepare dependencies
|
||||
- run: sh build-rector-scoped.sh
|
||||
|
||||
# 2. get tag - see https://github.com/WyriHaximus/github-action-get-previous-tag
|
||||
-
|
||||
id: previous_tag
|
||||
uses: "WyriHaximus/github-action-get-previous-tag@master"
|
||||
|
||||
# 3. publish it
|
||||
-
|
||||
uses: symplify/github-action-monorepo-split@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
with:
|
||||
package-directory: 'rector-scoped'
|
||||
split-repository-organization: 'rectorphp'
|
||||
split-repository-name: 'rector-prefixed'
|
||||
tag: ${{ steps.previous_tag.outputs.tag }}
|
||||
user-name: "kaizen-ci"
|
||||
user-email: "info@kaizen-ci.org"
|
|
@ -27,10 +27,14 @@ define('__RECTOR_RUNNING__', true);
|
|||
|
||||
// Require Composer autoload.php
|
||||
$autoloadIncluder = new AutoloadIncluder();
|
||||
$autoloadIncluder->includeCwdVendorAutoloadIfExists();
|
||||
$autoloadIncluder->autoloadProjectAutoloaderFile();
|
||||
$autoloadIncluder->includeDependencyOrRepositoryVendorAutoloadIfExists();
|
||||
|
||||
$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php');
|
||||
$autoloadIncluder->loadIfExistsAndNotLoadedYet(getcwd() . '/vendor/autoload.php');
|
||||
|
||||
$autoloadIncluder->autoloadProjectAutoloaderFile();
|
||||
$autoloadIncluder->autoloadFromCommandLine();
|
||||
$autoloadIncluder->autoloadPhpStanExtracted();
|
||||
|
||||
$symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesCaller());
|
||||
$symfonyStyle = $symfonyStyleFactory->create();
|
||||
|
@ -81,11 +85,6 @@ final class AutoloadIncluder
|
|||
*/
|
||||
private $alreadyLoadedAutoloadFiles = [];
|
||||
|
||||
public function includeCwdVendorAutoloadIfExists(): void
|
||||
{
|
||||
$this->loadIfExistsAndNotLoadedYet(getcwd() . '/vendor/autoload.php');
|
||||
}
|
||||
|
||||
public function includeDependencyOrRepositoryVendorAutoloadIfExists(): void
|
||||
{
|
||||
// Rector's vendor is already loaded
|
||||
|
@ -106,6 +105,14 @@ final class AutoloadIncluder
|
|||
$this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../../autoload.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* This autoloads extracted PHPStan autoload
|
||||
*/
|
||||
public function autoloadPhpStanExtracted(): void
|
||||
{
|
||||
$this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/phpstan/phpstan-extracted/vendor/autoload.php');
|
||||
}
|
||||
|
||||
public function autoloadFromCommandLine(): void
|
||||
{
|
||||
$cliArgs = $_SERVER['argv'];
|
||||
|
@ -124,7 +131,7 @@ final class AutoloadIncluder
|
|||
$this->loadIfExistsAndNotLoadedYet($fileToAutoload);
|
||||
}
|
||||
|
||||
private function loadIfExistsAndNotLoadedYet(string $filePath): void
|
||||
public function loadIfExistsAndNotLoadedYet(string $filePath): void
|
||||
{
|
||||
if (! file_exists($filePath)) {
|
||||
return;
|
||||
|
|
74
build-rector-scoped.sh
Normal file
74
build-rector-scoped.sh
Normal file
|
@ -0,0 +1,74 @@
|
|||
#!/bin/sh -l
|
||||
|
||||
# local-prefix test
|
||||
|
||||
# show errors
|
||||
set -e
|
||||
|
||||
# script fails if trying to access to an undefined variable
|
||||
set -u
|
||||
|
||||
|
||||
# functions
|
||||
note()
|
||||
{
|
||||
MESSAGE=$1;
|
||||
|
||||
printf "\n";
|
||||
echo "[NOTE] $MESSAGE";
|
||||
printf "\n";
|
||||
}
|
||||
|
||||
|
||||
note "Starts"
|
||||
|
||||
# configure here
|
||||
NESTED_DIRECTORY="rector-nested"
|
||||
SCOPED_DIRECTORY="rector-scoped"
|
||||
|
||||
|
||||
|
||||
# ---------------------------
|
||||
|
||||
|
||||
note "Coping root files to $NESTED_DIRECTORY directory"
|
||||
rsync -av * "$NESTED_DIRECTORY" --quiet
|
||||
|
||||
note "Running composer update without dev"
|
||||
composer update --no-dev --no-progress --ansi --working-dir "$NESTED_DIRECTORY" # --ignore-platform-req php
|
||||
|
||||
# Unpacking PHPStan
|
||||
note "Unpacking PHPStan"
|
||||
wget https://github.com/box-project/box/releases/download/3.11.0/box.phar -N --no-verbose
|
||||
php box.phar extract "$NESTED_DIRECTORY/vendor/phpstan/phpstan/phpstan.phar" "$NESTED_DIRECTORY/vendor/phpstan/phpstan-extracted"
|
||||
|
||||
# this will remove dependency on dev packages that are imported in phpstan.neon
|
||||
rm -f "$NESTED_DIRECTORY/phpstan-for-rector.neon"
|
||||
|
||||
# Avoid Composer v2 platform checks (composer.json requires PHP 7.4+, but below we are running 7.3)
|
||||
note "Disabling platform check"
|
||||
composer config platform-check false
|
||||
|
||||
# 2. scope it
|
||||
# @todo temporary only no net + is already locally insatlled
|
||||
note "Running scoper to $SCOPED_DIRECTORY"
|
||||
wget https://github.com/humbug/php-scoper/releases/download/0.14.0/php-scoper.phar -N --no-verbose
|
||||
|
||||
php php-scoper.phar add-prefix bin config packages rules src templates vendor composer.json --output-dir "../$SCOPED_DIRECTORY" --config scoper.php.inc --force --ansi --working-dir "$NESTED_DIRECTORY"
|
||||
|
||||
# keep only one PHPStan
|
||||
rm -rf "$SCOPED_DIRECTORY/vendor/phpstan/phpstan"
|
||||
|
||||
note "Dumping Composer Autoload"
|
||||
composer dump-autoload --working-dir "$SCOPED_DIRECTORY" --ansi --optimize --classmap-authoritative --no-dev
|
||||
|
||||
note "Scoping composer.json"
|
||||
composer require symplify/package-scoper
|
||||
vendor/bin/package-scoper scope-composer-json "$SCOPED_DIRECTORY/composer.json" --ansi
|
||||
|
||||
# clean up
|
||||
rm -rf "$NESTED_DIRECTORY"
|
||||
|
||||
|
||||
# copy metafiles needed for release
|
||||
cp -R scoped/. "$SCOPED_DIRECTORY"
|
|
@ -139,6 +139,7 @@
|
|||
"Rector\\PhpAttribute\\": "packages/php-attribute/src",
|
||||
"Rector\\PhpSpecToPHPUnit\\": "rules/php-spec-to-phpunit/src",
|
||||
"Rector\\Polyfill\\": "rules/polyfill/src",
|
||||
"Rector\\Compiler\\": "utils/compiler/src",
|
||||
"Rector\\PostRector\\": "packages/post-rector/src",
|
||||
"Rector\\Privatization\\": "rules/privatization/src",
|
||||
"Rector\\RectorGenerator\\": "packages/rector-generator/src",
|
||||
|
|
11
phpstan.neon
11
phpstan.neon
|
@ -538,7 +538,6 @@ parameters:
|
|||
paths:
|
||||
- packages/better-php-doc-parser/src/PhpDocNodeFactory/ParamPhpDocNodeFactory.php
|
||||
|
||||
|
||||
-
|
||||
message: '#Class cognitive complexity is 42, keep it under 40#'
|
||||
paths:
|
||||
|
@ -566,6 +565,16 @@ parameters:
|
|||
|
||||
- '#Content of method "mapToPhpParserNode\(\)" is duplicated with method in "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\ClosureTypeMapper" class\. Use unique content or abstract service instead#'
|
||||
|
||||
-
|
||||
message: '#Class cognitive complexity is \d+, keep it under 40#'
|
||||
paths:
|
||||
- src/Rector/AbstractRector.php
|
||||
- rules/php80/src/Rector/If_/NullsafeOperatorRector.php
|
||||
- rules/code-quality/src/Rector/For_/ForToForeachRector.php
|
||||
- rules/coding-style/src/Rector/Use_/RemoveUnusedAliasRector.php
|
||||
|
||||
- '#Content of method "mapToPhpParserNode\(\)" is duplicated with method in "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\ClosureTypeMapper" class\. Use unique content or abstract service instead#'
|
||||
|
||||
-
|
||||
message: '#Class cognitive complexity is \d+, keep it under 40#'
|
||||
paths:
|
||||
|
|
34
scoped/.github/workflows/code_analysis.yaml
vendored
Normal file
34
scoped/.github/workflows/code_analysis.yaml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
name: Code Analysis
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
code_analysis:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: ['7.3', '7.4', '8.0']
|
||||
actions:
|
||||
-
|
||||
name: Bare Run
|
||||
run: php bin/rector --ansi
|
||||
-
|
||||
name: Finalize Rule
|
||||
run: |
|
||||
composer require doctrine/orm
|
||||
php bin/rector process tests/fixture-finalize --config ci/rector-finalize.php --ansi
|
||||
|
||||
name: ${{ matrix.actions.name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: none
|
||||
|
||||
- run: ${{ matrix.actions.run }}
|
30
scoped/.github/workflows/composer_dependency.yaml
vendored
Normal file
30
scoped/.github/workflows/composer_dependency.yaml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
name: Composer Dependency
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
run_as_composer_dependency:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: ['7.3', '7.4', '8.0']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: none
|
||||
|
||||
- run: |
|
||||
mkdir standalone
|
||||
cd standalone
|
||||
# wait for deploy to packagist
|
||||
sleep 60
|
||||
- run: |
|
||||
cd standalone
|
||||
# run
|
||||
composer require rector/rector-prefixed:dev-master --dev --ansi
|
||||
vendor/bin/rector --debug --ansi
|
17
scoped/ci/rector-finalize.php
Normal file
17
scoped/ci/rector-finalize.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(FinalizeClassesWithoutChildrenRector::class);
|
||||
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::AUTOLOAD_PATHS, [
|
||||
__DIR__ . '/../tests/fixture-finalize'
|
||||
]);
|
||||
};
|
14
scoped/tests/fixture-finalize/SkipSomeEntity.php
Normal file
14
scoped/tests/fixture-finalize/SkipSomeEntity.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorPrefixed\Tests;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class SkipSomeEntity
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorPrefixed\Tests;
|
||||
|
||||
class SomeClassWithoutChildren
|
||||
{
|
||||
}
|
58
scoper.php.inc
Normal file
58
scoper.php.inc
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Compiler\PhpScoper\StaticEasyPrefixer;
|
||||
use Rector\Compiler\ValueObject\ScoperOption;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// [BEWARE] this path is relative to the root and location of this file
|
||||
$filePathsToSkip = [
|
||||
// @see https://github.com/rectorphp/rector/issues/2852#issuecomment-586315588
|
||||
'vendor/symfony/deprecation-contracts/function.php'
|
||||
];
|
||||
|
||||
// remove phpstan, because it is already prefixed in its own scope
|
||||
$finder = new Finder();
|
||||
$phpstanPhpFileInfos = $finder->files()
|
||||
->name('*.php')
|
||||
// the working dir is already "rector-nested"
|
||||
->in(__DIR__ . '/vendor/phpstan/phpstan-extracted')
|
||||
->getIterator();
|
||||
|
||||
foreach ($phpstanPhpFileInfos as $phpstanPhpFileInfo) {
|
||||
$filePathsToSkip[] = $phpstanPhpFileInfo->getRealPath();
|
||||
}
|
||||
|
||||
|
||||
// see https://github.com/humbug/php-scoper
|
||||
return [
|
||||
ScoperOption::FILES_WHITELIST => $filePathsToSkip,
|
||||
ScoperOption::WHITELIST => StaticEasyPrefixer::getExcludedNamespacesAndClasses(),
|
||||
ScoperOption::PATCHERS => [
|
||||
// [BEWARE] $filePath is absolute!
|
||||
|
||||
// related to Composer 2 naming - @todo why exactly is this needed?
|
||||
function (string $filePath, string $prefix, string $content): string {
|
||||
if (! Strings::endsWith($filePath, 'vendor/composer/autoload_real.php')) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content = str_replace(
|
||||
"'Composer\\\\Autoload\\\\ClassLoader",
|
||||
"'" . $prefix . '\\\\Composer\\\\Autoload\\\\ClassLoader',
|
||||
$content
|
||||
);
|
||||
|
||||
return $content;
|
||||
},
|
||||
|
||||
// fatal error on PHP 8
|
||||
function (string $filePath, string $prefix, string $content): string {
|
||||
return str_replace('private static final', 'private static', $content);
|
||||
},
|
||||
],
|
||||
];
|
29
utils/compiler/bin/cleaner.php
Normal file
29
utils/compiler/bin/cleaner.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
use Symplify\SmartFileSystem\FileSystemFilter;
|
||||
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
|
||||
use Symplify\SmartFileSystem\Finder\SmartFinder;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
require __DIR__ . '/../../../vendor/autoload.php';
|
||||
|
||||
// @todo complete later..., maybe decoupled to symplify?
|
||||
// @todo normal commadn with arugment?
|
||||
// cleans all useless files
|
||||
$smartFinder = new SmartFinder(new FinderSanitizer(), new FileSystemFilter());
|
||||
$sartFileSystem = new SmartFileSystem();
|
||||
|
||||
$fileInfos = $smartFinder->find([
|
||||
__DIR__ . '/../../../packages',
|
||||
__DIR__ . '/../../../src',
|
||||
__DIR__ . '/../../../rules',
|
||||
__DIR__ . '/../../../tests',
|
||||
], '#.(\.php\.inc|Test\.php)$#');
|
||||
|
||||
echo sprintf('Found %d files to be deleted', count($fileInfos));
|
||||
|
||||
foreach ($fileInfos as $fileInfo) {
|
||||
echo $fileInfo->getRelativeFilePathFromCwd() . PHP_EOL;
|
||||
}
|
||||
|
||||
$sartFileSystem->remove($fileInfos);
|
59
utils/compiler/src/PhpScoper/StaticEasyPrefixer.php
Normal file
59
utils/compiler/src/PhpScoper/StaticEasyPrefixer.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Compiler\PhpScoper;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
final class StaticEasyPrefixer
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public const EXCLUDED_CLASSES = [
|
||||
'Symfony\Component\Console\Style\SymfonyStyle',
|
||||
// part of public interface of configs.php
|
||||
'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator',
|
||||
// this is not prefixed on few places by php-scoper by default, probably some bug
|
||||
'Doctrine\Inflector\Inflector',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private const EXCLUDED_NAMESPACES = [
|
||||
// naturally
|
||||
'Rector\*',
|
||||
// we use this API a lot
|
||||
'PhpParser\*',
|
||||
'PHPStan\*',
|
||||
'Symplify\*',
|
||||
// doctrine annotations to autocomplete
|
||||
'Doctrine\ORM\Mapping\*',
|
||||
];
|
||||
|
||||
public static function prefixClass(string $class, string $prefix): string
|
||||
{
|
||||
foreach (self::EXCLUDED_NAMESPACES as $excludedNamespace) {
|
||||
$excludedNamespace = Strings::substring($excludedNamespace, 0, -2) . '\\';
|
||||
if (Strings::startsWith($class, $excludedNamespace)) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
if (Strings::startsWith($class, '@')) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
return $prefix . '\\' . $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getExcludedNamespacesAndClasses(): array
|
||||
{
|
||||
return array_merge(self::EXCLUDED_NAMESPACES, self::EXCLUDED_CLASSES);
|
||||
}
|
||||
}
|
31
utils/compiler/src/ValueObject/ScoperOption.php
Normal file
31
utils/compiler/src/ValueObject/ScoperOption.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Compiler\ValueObject;
|
||||
|
||||
/**
|
||||
* Based on https://github.com/humbug/php-scoper#configuration
|
||||
*/
|
||||
final class ScoperOption
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const FINDERS = 'finders';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PATCHERS = 'patchers';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const WHITELIST = 'whitelist';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const FILES_WHITELIST = 'files-whitelist';
|
||||
}
|
Loading…
Reference in New Issue
Block a user