diff --git a/Build/Scripts/checkIntegritySetLabels.php b/Build/Scripts/checkIntegritySetLabels.php new file mode 100755 index 0000000000000000000000000000000000000000..b001664560f72e61348c3209b9eb37315ae7b94b --- /dev/null +++ b/Build/Scripts/checkIntegritySetLabels.php @@ -0,0 +1,135 @@ +#!/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! + */ + +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Yaml\Yaml; + +require __DIR__ . '/../../vendor/autoload.php'; + +if (PHP_SAPI !== 'cli') { + die('Script must be called from command line.' . chr(10)); +} + +final readonly class CheckIntegritySetLabels +{ + public function execute(): int + { + $filesToProcess = $this->findSetLabels(); + $output = new ConsoleOutput(); + + $resultAcrossAllFiles = 0; + $testResults = []; + /** @var \SplFileInfo $labelFile */ + foreach ($filesToProcess as $labelFile) { + $fullFilePath = $labelFile->getRealPath(); + $result = $this->checkValidLabels($fullFilePath); + if ($result !== null) { + $testResults[] = $result; + } + } + if ($testResults === []) { + return 0; + } + + $table = new Table($output); + $table->setHeaders([ + 'EXT', + 'Set', + 'Invalid set labels', + 'Missing set labels', + ]); + foreach ($testResults as $result) { + $table->addRow([ + $result['ext'], + $result['set'], + implode("\n", $result['invalid']), + implode("\n", $result['missing']), + ]); + } + $table->render(); + return 1; + } + + private function findSetLabels(): Finder + { + $finder = new Finder(); + $labelFiles = $finder + ->files() + ->in(__DIR__ . '/../../typo3/sysext/*/Configuration/Sets/*') + ->name('labels.xlf'); + return $labelFiles; + } + + private function checkValidLabels(string $labelFile): ?array + { + $extensionKey = $this->extractExtensionKey($labelFile); + $doc = new DOMDocument(); + if (!$doc->load($labelFile)) { + throw new \RuntimeException('Failed to load xlf file: ' . $labelFile, 1725902515); + } + + $requiredLabels = [ + 'label', + ]; + $optionalLabels = [ + 'description', + ]; + + $settingsDefinitions = Yaml::parseFile(dirname($labelFile) . '/settings.definitions.yaml'); + foreach ($settingsDefinitions['settings'] as $key => $settingsDefinition) { + $requiredLabels[] = 'settings.' . $key; + $optionalLabels[] = 'settings.description.' . $key; + } + + $setName = Yaml::parseFile(dirname($labelFile) . '/config.yaml')['name']; + + $availableLabels = []; + foreach ($doc->getElementsByTagName('trans-unit') as $tu) { + $availableLabels[] = $tu->getAttribute('id'); + } + + $allowedLabels = [ + ...$requiredLabels, + ...$optionalLabels, + ]; + $missing = array_diff($requiredLabels, $availableLabels); + $invalid = array_diff($availableLabels, $allowedLabels); + + if ($missing === [] && $invalid === []) { + return null; + } + + return [ + 'ext' => $extensionKey, + 'set' => $setName, + 'invalid' => $invalid, + 'missing' => $missing, + ]; + } + + 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']; + } +} + +exit((new CheckIntegritySetLabels())->execute()); diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index b6d78fb7c9d4c93959a655ff3dd455aa98b15eec..9034dc22644547787f3c382ec63da2ca4e02e670 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -198,6 +198,7 @@ Options: - cglHeaderGit: test and fix latest committed patch for CGL file header compliance - checkBom: check UTF-8 files do not contain BOM - checkComposer: check composer.json files for version integrity + - checkIntegritySetLabels: check labels.xlf file integrity of site sets - checkExtensionScannerRst: test all .rst files referenced by extension scanner exist - checkFilePathLength: test core file paths do not exceed maximum length - checkGitSubmodule: test core git has no sub modules defined @@ -893,6 +894,10 @@ case ${TEST_SUITE} in ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-utf8bom-${SUFFIX} ${IMAGE_PHP} Build/Scripts/checkUtf8Bom.sh SUITE_EXIT_CODE=$? ;; + checkIntegritySetLabels) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-integrity-set-labels-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/checkIntegritySetLabels.php + SUITE_EXIT_CODE=$? + ;; checkComposer) ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-composer-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/checkIntegrityComposer.php SUITE_EXIT_CODE=$? diff --git a/Build/gitlab-ci/nightly/integrity.yml b/Build/gitlab-ci/nightly/integrity.yml index 32ae1708fd687b8100f71a9b5c50486e91571bd8..3ce3cc76bb7b67ce54be907341c01b41a1f77a6e 100644 --- a/Build/gitlab-ci/nightly/integrity.yml +++ b/Build/gitlab-ci/nightly/integrity.yml @@ -29,6 +29,7 @@ integration various php 8.4: - Build/Scripts/runTests.sh -s checkBom -p 8.4 - Build/Scripts/runTests.sh -s checkComposer -p 8.4 - Build/Scripts/runTests.sh -s checkIntegrityPhp -p 8.4 + - Build/Scripts/runTests.sh -s checkIntegritySetLabels -p 8.4 - Build/Scripts/runTests.sh -s lintServicesYaml -p 8.4 - Build/Scripts/runTests.sh -s lintYaml -p 8.4 diff --git a/Build/gitlab-ci/pre-merge/integrity.yml b/Build/gitlab-ci/pre-merge/integrity.yml index 163f518a3f319db353250f5008b47b0b695501d9..a2e949ac0e23e2b2014dda012780994cc095761b 100644 --- a/Build/gitlab-ci/pre-merge/integrity.yml +++ b/Build/gitlab-ci/pre-merge/integrity.yml @@ -31,6 +31,7 @@ integration various php 8.2 pre-merge: - Build/Scripts/runTests.sh -s checkBom -p 8.2 - Build/Scripts/runTests.sh -s checkComposer -p 8.2 - Build/Scripts/runTests.sh -s checkIntegrityPhp -p 8.2 + - Build/Scripts/runTests.sh -s checkIntegritySetLabels -p 8.2 - Build/Scripts/runTests.sh -s lintServicesYaml -p 8.2 - Build/Scripts/runTests.sh -s lintYaml -p 8.2