From 498f4118f70f451b93c63c75a77fdd8abc8ee7c5 Mon Sep 17 00:00:00 2001 From: Mathias Schreiber <mathias.schreiber@typo3.org> Date: Sat, 17 Feb 2018 21:40:05 +0100 Subject: [PATCH] [TASK] Add composer.json integrity check Each composer.json file in a system extension now has its dependencies checked against the root composer.json to avoid errors after the subtree split. Resolves: #83957 Releases: master, 8.7 Change-Id: Ibf37bd56fd1534b14e714dfdfaaf6374a48978c4 Reviewed-on: https://review.typo3.org/55785 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- Build/Scripts/checkIntegrityComposer.php | 150 ++++++++++++++++++ .../src/main/java/core/AbstractCoreSpec.java | 11 +- typo3/sysext/core/composer.json | 2 +- typo3/sysext/fluid/composer.json | 2 +- typo3/sysext/install/composer.json | 2 +- typo3/sysext/redirects/composer.json | 2 +- 6 files changed, 163 insertions(+), 6 deletions(-) create mode 100755 Build/Scripts/checkIntegrityComposer.php diff --git a/Build/Scripts/checkIntegrityComposer.php b/Build/Scripts/checkIntegrityComposer.php new file mode 100755 index 000000000000..a3f12f19e373 --- /dev/null +++ b/Build/Scripts/checkIntegrityComposer.php @@ -0,0 +1,150 @@ +#!/usr/bin/env php +<?php +declare(strict_types = 1); +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +require __DIR__ . '/../../vendor/autoload.php'; + +if (PHP_SAPI !== 'cli') { + die('Script must be called from command line.' . chr(10)); +} + +/** + * Core integrity test script: + * + * Find all composer.json files in all system extensions and compare + * their dependencies against the defined dependencies of our root + * composer.json + */ +class checkIntegrityComposer +{ + /** + * @var array + */ + private $rootComposerJson = []; + + private $testResults = []; + + /** + * Executes the composer integrity check. + * The return value is used directly in the ext() call outside this class. + * + * @return int + */ + public function execute(): int + { + $rootComposerJson = __DIR__ . '/../../composer.json'; + $this->rootComposerJson = json_decode(file_get_contents($rootComposerJson), true); + $filesToProcess = $this->findExtensionComposerJson(); + $output = new \Symfony\Component\Console\Output\ConsoleOutput(); + + $resultAcrossAllFiles = 0; + /** @var \SplFileInfo $composerJsonFile */ + foreach ($filesToProcess as $composerJsonFile) { + $fullFilePath = $composerJsonFile->getRealPath(); + $this->validateComposerJson($fullFilePath); + } + if (!empty($this->testResults)) { + $table = new \Symfony\Component\Console\Helper\Table($output); + $table->setHeaders([ + 'EXT', + 'type', + 'Dependency', + 'should be', + 'actually is' + ]); + foreach ($this->testResults as $extKey => $results) { + foreach ($results as $result) { + $table->addRow([ + $extKey, + $result['type'], + $result['dependency'], + $result['shouldBe'], + $result['actuallyIs'] + ]); + } + } + $table->render(); + $resultAcrossAllFiles = 1; + } + return $resultAcrossAllFiles; + } + + /** + * Finds all composer.json files in TYPO3s system extensions + * + * @return \Symfony\Component\Finder\Finder + */ + private function findExtensionComposerJson(): \Symfony\Component\Finder\Finder + { + $finder = new Symfony\Component\Finder\Finder(); + $composerFiles = $finder + ->files() + ->in(__DIR__ . '/../../typo3/sysext/*') + ->name('composer.json'); + return $composerFiles; + } + + /** + * Checks if the dependencies defined in $composerJsonFile are the same as + * in TYPO3s root composer.json file. + * + * @param string $composerJsonFile + */ + private function validateComposerJson(string $composerJsonFile) + { + $extensionKey = $this->extractExtensionKey($composerJsonFile); + $extensionComposerJson = json_decode(file_get_contents($composerJsonFile), true); + // Check require section + foreach ($this->rootComposerJson['require'] as $requireKey => $requireItem) { + if (isset($extensionComposerJson['require'][$requireKey]) && $extensionComposerJson['require'][$requireKey] !== $requireItem) { + // log inconsistency + $this->testResults[$extensionKey][] = [ + 'type' => 'require', + 'dependency' => $requireKey, + 'shouldBe' => $requireItem, + 'actuallyIs' => $extensionComposerJson['require'][$requireKey] + ]; + } + } + // Check require-dev section + foreach ($this->rootComposerJson['require-dev'] as $requireDevKey => $requireDevItem) { + if (isset($extensionComposerJson['require-dev'][$requireDevKey]) && $extensionComposerJson['require-dev'][$requireDevKey] !== $requireDevItem) { + // log inconsistency + $this->testResults[$extensionKey][] = [ + 'type' => 'require-dev', + 'dependency' => $requireDevKey, + 'shouldBe' => $requireDevItem, + 'actuallyIs' => $extensionComposerJson['require-dev'][$requireDevKey] + ]; + } + } + } + + /** + * Makes the output on CLI a bit more readable + * + * @param string $filename + * @return string + */ + private function extractExtensionKey(string $filename): string + { + $pattern = '/typo3\/sysext\/(?<extName>[a-z].+?)\//'; + preg_match_all($pattern, $filename, $matches, PREG_SET_ORDER, 0); + return $matches[0]['extName']; + } +} + +$composerIntegrityChecker = new checkIntegrityComposer(); +exit($composerIntegrityChecker->execute()); diff --git a/Build/bamboo/src/main/java/core/AbstractCoreSpec.java b/Build/bamboo/src/main/java/core/AbstractCoreSpec.java index 3c7e41607c5b..2b06cf814f17 100644 --- a/Build/bamboo/src/main/java/core/AbstractCoreSpec.java +++ b/Build/bamboo/src/main/java/core/AbstractCoreSpec.java @@ -516,8 +516,15 @@ abstract public class AbstractCoreSpec { .inlineBody( this.getScriptTaskBashInlineBody() + "./Build/Scripts/checkIntegrityCsvFixtures.php" - ) - ) + ), + new ScriptTask() + .description("Run composer.json integrity check") + .interpreter(ScriptTaskProperties.Interpreter.BINSH_OR_CMDEXE) + .inlineBody( + this.getScriptTaskBashInlineBody() + + "./Build/Scripts/checkIntegrityComposer.php" + ) + ) .requirements( this.getRequirementPhpVersion72() ) diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json index 450819e0e77e..ff3829cb2696 100644 --- a/typo3/sysext/core/composer.json +++ b/typo3/sysext/core/composer.json @@ -33,7 +33,7 @@ "mso/idna-convert": "^1.1.0", "typo3fluid/fluid": "^2.4", "guzzlehttp/guzzle": "^6.3.0", - "doctrine/dbal": "~2.6", + "doctrine/dbal": "^2.6", "nikic/php-parser": "^3.1", "symfony/polyfill-intl-icu": "^1.6" }, diff --git a/typo3/sysext/fluid/composer.json b/typo3/sysext/fluid/composer.json index 3cbcb3c16971..e17530b3dad6 100644 --- a/typo3/sysext/fluid/composer.json +++ b/typo3/sysext/fluid/composer.json @@ -13,7 +13,7 @@ "require": { "typo3/cms-core": "9.2.*@dev", "typo3/cms-extbase": "9.2.*@dev", - "typo3fluid/fluid": "^2.3" + "typo3fluid/fluid": "^2.4" }, "conflict": { "typo3/cms": "*" diff --git a/typo3/sysext/install/composer.json b/typo3/sysext/install/composer.json index e6afbd3e70f8..9e1e02446b77 100644 --- a/typo3/sysext/install/composer.json +++ b/typo3/sysext/install/composer.json @@ -14,7 +14,7 @@ "typo3/cms-core": "9.2.*@dev", "typo3/cms-extbase": "9.2.*@dev", "typo3/cms-fluid": "9.2.*@dev", - "nikic/php-parser": "~3.1", + "nikic/php-parser": "^3.1", "symfony/finder": "^2.7 || ^3.0 || ^4.0" }, "conflict": { diff --git a/typo3/sysext/redirects/composer.json b/typo3/sysext/redirects/composer.json index 592267c0bc18..8e360fed6ae0 100644 --- a/typo3/sysext/redirects/composer.json +++ b/typo3/sysext/redirects/composer.json @@ -12,7 +12,7 @@ "require": { "typo3/cms-core": "9.2.*@dev", "typo3/cms-backend": "9.2.*@dev", - "typo3fluid/fluid": "^2.3" + "typo3fluid/fluid": "^2.4" }, "conflict": { "typo3/cms": "*" -- GitLab