[Prefixed Rector] Add package-scoper approach (#4559)

This commit is contained in:
Tomas Votruba 2020-12-26 00:17:31 +01:00 committed by GitHub
parent 4e06c46253
commit d4883d44d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 467 additions and 9 deletions

View 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"

View 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"

View File

@ -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
View 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"

View File

@ -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",

View File

@ -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:

View 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 }}

View 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

View 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'
]);
};

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Rector\RectorPrefixed\Tests;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class SkipSomeEntity
{
}

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\RectorPrefixed\Tests;
class SomeClassWithoutChildren
{
}

58
scoper.php.inc Normal file
View 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);
},
],
];

View 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);

View 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);
}
}

View 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';
}