diff --git a/app/autoload.php b/app/autoload.php new file mode 100644 index 0000000..2256f74 --- /dev/null +++ b/app/autoload.php @@ -0,0 +1,11 @@ +validateWritablePath($cachePath)) { - throw new Exception('Cache dir is not writable.'); + throw new \Exception('Cache dir is not writable.'); } } } diff --git a/app/code/Deploy/Instance.php b/app/code/Deploy/Instance.php index 1a840ee..67c632e 100644 --- a/app/code/Deploy/Instance.php +++ b/app/code/Deploy/Instance.php @@ -1,6 +1,13 @@ $instancePath) { $this->instanceList[$groupName][] = is_int($instancePath) ? $instancePath - : new Deploy_Instance($instanceName, $instancePath); + : new Instance($instanceName, $instancePath); } } diff --git a/app/code/Design.php b/app/code/Design.php index 7342523..af355cd 100644 --- a/app/code/Design.php +++ b/app/code/Design.php @@ -1,6 +1,13 @@ loadCache(self::STATIC_HASH_CACHE_ID); if (!$content) { $content = $this->generateStaticHash(); diff --git a/app/code/File/Filesystem.php b/app/code/File/Filesystem.php index 495cbbe..48ba244 100644 --- a/app/code/File/Filesystem.php +++ b/app/code/File/Filesystem.php @@ -1,4 +1,11 @@ getHashedFileName($name) . '.patch'; $patchPath = $this->_uploadPath . $newFileName[$fileId]; if (move_uploaded_file($_FILES[$fileElementName]['tmp_name'][$fileId], $patchPath)) { - require_once 'app/code/Patch/Converter.php'; - $converter = new Patch_Converter(); + $converter = new Converter(); $converter->preparePatch($patchPath); } else { $error[$fileId][] = 'Unable to move uploaded file from tmp folder.'; diff --git a/app/code/Patch/AbstractChecker.php b/app/code/Patch/AbstractChecker.php new file mode 100644 index 0000000..8827643 --- /dev/null +++ b/app/code/Patch/AbstractChecker.php @@ -0,0 +1,73 @@ +instanceManager = $instanceManager; + } + + /** + * Return patch status for each configured version. + * + * @param string $patch + * @return array + */ + public function check(string $patch) + { + $result = []; + foreach ($this->instanceManager->getInstanceList() as $groupName => $groupInstanceList) { + foreach ($groupInstanceList as $instance) { + if (is_int($instance)) { + for ($i = 0; $i < $instance; $i++) { + $result[$groupName][] = ['instance_name' => 'n/a', 'result' => 'n/a']; + } + } elseif ($instance->getInstanceType() == Instance::INSTANCE_TYPE_INVALID) { + $result[$groupName][] = ['instance_name' => $instance->getInstanceName(), 'result' => 'n/a']; + } else { + $result[$groupName][] = [ + 'instance_name' => $instance->getInstanceName(), + 'result' => $this->getResult($instance, $patch) + ]; + } + } + } + + return $result; + } + + /** + * Get status of the patch + * + * @param Instance $instance + * @param string $patch + * @return array|string + */ + abstract public function getResult(Instance $instance, string $patch); +} diff --git a/app/code/Patch/Check/Strategy/AbstractStrategy.php b/app/code/Patch/Check/Strategy/AbstractStrategy.php index 010017d..d65ffe6 100644 --- a/app/code/Patch/Check/Strategy/AbstractStrategy.php +++ b/app/code/Patch/Check/Strategy/AbstractStrategy.php @@ -1,4 +1,13 @@ executeCommand($this->getCommand($patchPath, $instancePath)); if (!$result) { - return Patch_Checker::PATCH_APPLY_RESULT_SUCCESSFUL; + return Checker::PATCH_APPLY_RESULT_SUCCESSFUL; } $result = $this->executeCommand($this->getCommand($patchPath, $instancePath, true)); if (!$result) { - return Patch_Checker::PATCH_APPLY_RESULT_MERGED; + return Checker::PATCH_APPLY_RESULT_MERGED; } + + return Checker::PATCH_APPLY_RESULT_FAILED; } } diff --git a/app/code/Patch/Check/Strategy/GitApplyStrategy.php b/app/code/Patch/Check/Strategy/GitApplyStrategy.php index 7bcfeef..9eafb5b 100644 --- a/app/code/Patch/Check/Strategy/GitApplyStrategy.php +++ b/app/code/Patch/Check/Strategy/GitApplyStrategy.php @@ -1,4 +1,11 @@ originalPatchPath = $patchPath; - $this->instanceManager = new Deploy_InstanceManager(); - $this->strategyManager = new StrategyManager(); - $this->patchConverter = new Patch_Converter(); - } - - private function getPatchForInstanceType($instanceType) - { - if (!isset($this->patchPerInstanceType[$instanceType])) { - $patchPath = false; - if ($instanceType == Deploy_Instance::INSTANCE_TYPE_GIT) { - $patchPath = BP . UPLOAD_PATH . pathinfo($this->originalPatchPath, PATHINFO_FILENAME) . '.git.patch'; - $isConverted = $this->patchConverter->convertFromComposerToGitFormat($this->originalPatchPath, $patchPath); - if (!$isConverted) { - $patchPath = false; - } - } elseif ($instanceType == Deploy_Instance::INSTANCE_TYPE_COMPOSER) { - $patchPath = BP . UPLOAD_PATH . pathinfo($this->originalPatchPath, PATHINFO_FILENAME) . '.composer.patch'; - $isConverted = $this->patchConverter->convertFromGitToComposerFormat($this->originalPatchPath, $patchPath); - if (!$isConverted) { - $patchPath = false; - } - } - - $this->patchPerInstanceType[$instanceType] = $patchPath; - } - - return $this->patchPerInstanceType[$instanceType]; + /** + * @param InstanceManager $instanceManager + * @param StrategyManager $strategyManager + * @param InstancePatchConverter $patchConverter + */ + public function __construct( + InstanceManager $instanceManager, + StrategyManager $strategyManager, + InstancePatchConverter $patchConverter + ) { + parent::__construct($instanceManager); + $this->patchConverter = $patchConverter; + $this->strategyManager = $strategyManager; } - public function checkPatchForAllReleases() + /** + * @inheritDoc + */ + public function getResult(Instance $instance, string $patch) { - $result = []; - foreach ($this->instanceManager->getInstanceList() as $groupName => $groupInstanceList) { - foreach ($groupInstanceList as $instance) { - if (is_int($instance)) { - for ($i = 0; $i < $instance; $i++) { - $result[$groupName][] = ['instance_name' => 'n/a', 'check_strategy' => 'n/a']; - } - continue; - } - if ($instance->getInstanceType() == Deploy_Instance::INSTANCE_TYPE_INVALID) { - $result[$groupName][] = ['instance_name' => $instance->getInstanceName(), 'check_strategy' => 'n/a']; - continue; - } - - $patchForInstancePath = $this->getPatchForInstanceType($instance->getInstanceType()); - $checkResult = []; - foreach ($this->strategyManager->getStrategyList() as $strategy) { - $patchPath = ($strategy->getIsPreserveOriginalFileFormat()) - ? $this->originalPatchPath - : $patchForInstancePath; - - $strategyResult = $strategy->check($patchPath, $instance->getInstancePath()); - - if ($strategyResult == self::PATCH_APPLY_RESULT_MERGED) { - $checkResult = 'merged'; - break; - } - - $checkResult[$strategy->getStrategyName()] = $strategyResult; - } - - $result[$groupName][] = [ - 'instance_name' => $instance->getInstanceName(), - 'check_strategy' => $checkResult - ]; + $patchForInstancePath = $this->patchConverter->convert($patch, $instance->getInstanceType()); + $checkResult = []; + foreach ($this->strategyManager->getStrategyList() as $strategy) { + $patchPath = ($strategy->getIsPreserveOriginalFileFormat()) + ? $patch + : $patchForInstancePath; + $strategyResult = $strategy->check($patchPath, $instance->getInstancePath()); + + if ($strategyResult === self::PATCH_APPLY_RESULT_MERGED) { + $checkResult = 'merged'; + break; } + $checkResult[$strategy->getStrategyName()] = $strategyResult; } - return $result; + return $checkResult; } } diff --git a/app/code/Patch/Converter.php b/app/code/Patch/Converter.php index 0bbd1f9..34455e6 100644 --- a/app/code/Patch/Converter.php +++ b/app/code/Patch/Converter.php @@ -1,8 +1,15 @@ patchConverter = $patchConverter; + } + + /** + * Convert patch to the format compatible with given instance type + * + * @param string $originalPatchPath + * @param string $instanceType + * @return false|string + */ + public function convert(string $originalPatchPath, string $instanceType) + { + if (!isset($this->cache[$originalPatchPath]) || !isset($this->cache[$originalPatchPath][$instanceType])) { + $patchPath = false; + if ($instanceType === Instance::INSTANCE_TYPE_GIT) { + $patchPath = BP . UPLOAD_PATH . pathinfo($originalPatchPath, PATHINFO_FILENAME) . '.git.patch'; + $isConverted = $this->patchConverter->convertFromComposerToGitFormat($originalPatchPath, $patchPath); + if (!$isConverted) { + $patchPath = false; + } + } elseif ($instanceType === Instance::INSTANCE_TYPE_COMPOSER) { + $patchPath = BP . UPLOAD_PATH . pathinfo($originalPatchPath, PATHINFO_FILENAME) . '.composer.patch'; + $isConverted = $this->patchConverter->convertFromGitToComposerFormat($originalPatchPath, $patchPath); + if (!$isConverted) { + $patchPath = false; + } + } + + $this->cache[$originalPatchPath][$instanceType] = $patchPath; + } + + return $this->cache[$originalPatchPath][$instanceType]; + } +} diff --git a/app/code/Patch/MQP/Checker.php b/app/code/Patch/MQP/Checker.php new file mode 100644 index 0000000..37e8759 --- /dev/null +++ b/app/code/Patch/MQP/Checker.php @@ -0,0 +1,63 @@ +versionsManager = $versionsManager; + $this->patchRepository = $patchRepository; + } + + /** + * @inheritDoc + */ + public function getResult(Instance $instance, string $patch): int + { + $aggregatedPatch = $this->patchRepository->findOne($patch); + $status = self::PATCH_APPLY_RESULT_FAILED; + foreach ($aggregatedPatch->getPatches() as $patch) { + $packageVersion = $this->versionsManager->getPackageVersion( + $instance->getInstanceName(), + $patch->getPackageName() + ); + if ($packageVersion && Semver::satisfies($packageVersion, $patch->getPackageConstraint())) { + $status = self::PATCH_APPLY_RESULT_SUCCESSFUL; + break; + } + } + return $status; + } +} diff --git a/app/code/Patch/MQP/Data/AggregatedPatch.php b/app/code/Patch/MQP/Data/AggregatedPatch.php new file mode 100644 index 0000000..1bda868 --- /dev/null +++ b/app/code/Patch/MQP/Data/AggregatedPatch.php @@ -0,0 +1,52 @@ +config = $config; + } + + /** + * Get patch items + * + * @return Patch[] + */ + public function getPatches(): array + { + if ($this->patches === null) { + $this->patches = []; + foreach ($this->config as $packageName => $packageConfiguration) { + foreach ($packageConfiguration as $patchTitle => $patchInfo) { + foreach ($patchInfo as $packageConstraint => $patchData) { + $this->patches[] = new Patch($packageName, $packageConstraint, $patchData['file']); + } + } + } + } + + return $this->patches; + } +} diff --git a/app/code/Patch/MQP/Data/Patch.php b/app/code/Patch/MQP/Data/Patch.php new file mode 100644 index 0000000..db9a0a0 --- /dev/null +++ b/app/code/Patch/MQP/Data/Patch.php @@ -0,0 +1,72 @@ +packageName = $packageName; + $this->packageConstraint = $packageConstraint; + $this->filename = $filename; + } + + /** + * Get package name + * + * @return string + */ + public function getPackageName(): string + { + return $this->packageName; + } + + /** + * Get package constraints + * + * @return string + */ + public function getPackageConstraint(): string + { + return $this->packageConstraint; + } + + /** + * Get patch filename + * + * @return string + */ + public function getFilename(): string + { + return $this->filename; + } +} diff --git a/app/code/Patch/MQP/PatchRepository.php b/app/code/Patch/MQP/PatchRepository.php new file mode 100644 index 0000000..873cb2f --- /dev/null +++ b/app/code/Patch/MQP/PatchRepository.php @@ -0,0 +1,65 @@ +info = $info; + } + + + /** + * Find patch by ID + * + * @param string $id + * @return AggregatedPatch + * @throws \Exception + */ + public function findOne(string $id): AggregatedPatch + { + $config = $this->getConfiguration(); + if (isset($config[$id])) { + return new AggregatedPatch($config[$id]); + } + throw new \Exception("Patch '$id' cannot be found."); + } + + /** + * Get patches configuration + * + * @return array + * @throws \Exception + */ + private function getConfiguration(): array + { + $result = []; + $configPath = $this->info->getPatchesConfig(); + if (file_exists($configPath)) { + $result = Util::getJsonFile($configPath); + } + + return $result; + } +} diff --git a/app/code/Patch/MQP/Version.php b/app/code/Patch/MQP/Version.php new file mode 100644 index 0000000..defee78 --- /dev/null +++ b/app/code/Patch/MQP/Version.php @@ -0,0 +1,32 @@ +getConfiguration()[$coreVersion][$packageName] ?? null; + } + + /** + * Get versions configuration + * + * @return array + */ + private function getConfiguration(): array + { + if ($this->configuration === null) { + $this->configuration = []; + $configPath = $this->getConfigurationPath(); + if (file_exists($configPath)) { + $this->configuration = Util::getJsonFile($configPath); + } + } + + return $this->configuration; + } + + /** + * Get versions configuration path + * + * @return string + */ + private function getConfigurationPath(): string + { + return Util::getAbsolutePath('app/config/magento_package_versions.json'); + } +} diff --git a/app/code/Util.php b/app/code/Util.php new file mode 100644 index 0000000..cf052aa --- /dev/null +++ b/app/code/Util.php @@ -0,0 +1,47 @@ +' + + '' + + '' + + '' + groupName + '' + + '' + + '' + + (result.check_method !== 'mqp' + ? 'Version' + : 'Version' + ) + + (result.check_method !== 'mqp' + ? 'Is Compatible' + : 'Is Compatible' + ) + + '' + + (result.check_method !== 'mqp' + ? 'using Patchusing Git' + : '' + ) + + ''; + + for (release in groupResults) { + release = groupResults[release]; + + output += '' + + 'Merged'; + } else if (release.result === 1) { + output += '>' + release.instance_name + '' + + 'Yes'; + } else if (release.result === 0) { + output += '>' + release.instance_name + '' + + 'No'; + } else { + falseResultClass = 'td_fail'; + for (strategyResult in release.result) { + if (release.result[strategyResult] === 1) { + falseResultClass = 'td_adaptation_required'; + break; + } + } + + output += '>' + release.instance_name + ''; + if (release.result['patch'] === 1) { + output += 'Yes'; + } else { + output += 'No'; + } + if (release.result['git_apply'] === 1) { + output += 'Yes'; + } else { + output += 'No'; + } + } + output += ''; + } + output += ''; - $('#legend_header_div').click(function () { - $('#legend_body_div').toggle(); - $('#legend_fold_icon').toggleClass('rotate180'); - }) + $('#results_div').append(output); + } + } }) diff --git a/design/js/uploader.js b/design/js/uploader.js index 2e82c7b..4785adb 100755 --- a/design/js/uploader.js +++ b/design/js/uploader.js @@ -28,63 +28,7 @@ $().ready(function() { loaded : function(result) { $('#PRO' + result.fileno).remove(); $('#FILE' + result.fileno).html('Uploaded: ' + result.filename + ' (' + result.size + ')'); - - for (var groupName in result.check_results) { - var groupResults = result.check_results[groupName]; - - var output = '' - + '' - + '' - + '' - + '' - + '' - + '' - + '' - + '' - + '' - + ''; - - for (var release in groupResults) { - var release = groupResults[release]; - - output += '' - + ''; - if (release.check_strategy['patch'] == 1) { - output += ''; - } - output += '
' + groupName + '
VersionEECloud
Merged'; - } else { - var falseResultClass = 'td_fail'; - for (var strategyResult in release.check_strategy) { - if (release.check_strategy[strategyResult] == 1) { - falseResultClass = 'td_adaptation_required'; - break; - } - } - - output += '>' + release.instance_name + 'Ok'; - } else { - output += 'No'; - } - if (release.check_strategy['git_apply'] == 1) { - output += 'Ok'; - } else { - output += 'No'; - } - } - output += '
'; - - $('#results_div').append(output); - } + $('#upload').trigger('uploaded', [result]); }, progress : function(result) { diff --git a/design/style/style.css b/design/style/style.css index 6088c50..230276b 100755 --- a/design/style/style.css +++ b/design/style/style.css @@ -9,6 +9,39 @@ div { float: left; } +div#mqp_form_container, div#separator { + clear: both; + margin-top: 10px; +} + +div#mqp_form_container { + width: 760px; + border: 1px solid #ddd; + padding: 10px; +} + +div#separator { + width: 760px; + text-align: center; +} + +form#mqp_form{ + margin-top: 10px; +} + +span#mqp_error_patch_id { + display: block; + margin: 5px 0; + font-size: 12px; + color: red; +} + +span#mqp_version { + display: block; + margin: 5px 0; + font-size: 10px; +} + div#results_div { clear: both; padding: 32px; @@ -45,6 +78,7 @@ table.result_table { display: block; float: left; margin-right: 40px; + margin-top: 20px; border-collapse: collapse; } td.td_fail { diff --git a/design/templates/index.phtml b/design/templates/index.phtml index 1c2969f..2440e76 100755 --- a/design/templates/index.phtml +++ b/design/templates/index.phtml @@ -21,42 +21,17 @@
-
- - $group): ?> - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -
- - +
-OR-
+
+ Enter MQP Patch ID +
+ + + MQP version + +
- +
Legend
@@ -69,7 +44,7 @@ - Ok + Yes Patch is applicable. @@ -102,7 +77,7 @@ 2.1.0 - Ok + Yes No Validation was made for the specific release and the results are provided. diff --git a/index.php b/index.php index e991d0e..6942bc6 100755 --- a/index.php +++ b/index.php @@ -6,16 +6,18 @@ try { if ($action == 'upload' && !empty($_POST)) { - require_once 'app/code/File/Uploader.php'; - $fileUploader = new File_Uploader(['upload_path' => BP . UPLOAD_PATH]); + $fileUploader = new \Magento\PatchChecker\File\Uploader(['upload_path' => BP . UPLOAD_PATH]); $result = $fileUploader->upload(); - require_once 'app/code/Patch/Checker.php'; - $patchChecker = new Patch_Checker(BP . UPLOAD_PATH . $result['new_file_name'][0]); - $checkResults = $patchChecker->checkPatchForAllReleases(); + $patchChecker = new \Magento\PatchChecker\Patch\Checker( + new \Magento\PatchChecker\Deploy\InstanceManager(), + new \Magento\PatchChecker\Patch\Check\StrategyManager(), + new \Magento\PatchChecker\Patch\InstancePatchConverter(new \Magento\PatchChecker\Patch\Converter()) + ); + $checkResults = $patchChecker->check(BP . UPLOAD_PATH . $result['new_file_name'][0]); $result = $result['result']; $result['check_results'] = $checkResults; - + $result['check_method'] = 'file'; // checked patches statistic collection if (isset($result['filename'])) { $statsPath = BP . STATS_PATH; @@ -27,6 +29,27 @@ } } + echo json_encode($result); + die; + } elseif (!empty($_POST['patch_id'])) { + $result = [ + 'check_results' => [], + 'check_method' => 'mqp', + 'error' => '', + ]; + try { + $patchChecker = new \Magento\PatchChecker\Patch\MQP\Checker( + new \Magento\PatchChecker\Deploy\InstanceManager(), + new \Magento\PatchChecker\Patch\MQP\PatchRepository(new \Magento\QualityPatches\Info()), + new \Magento\PatchChecker\Patch\MQP\VersionsManager + ); + $result['check_results'] = $patchChecker->check($_POST['patch_id']); + $result['check_method'] = 'mqp'; + } catch (\Exception $exception) { + $mqpVersion = new \Magento\PatchChecker\Patch\MQP\Version(); + $result['error'] = "Patch ID '{$_POST['patch_id']}' is not found in MQP $mqpVersion"; + } + echo json_encode($result); die; } @@ -34,7 +57,7 @@ // @TODO Implement logging } -require_once 'app/code/Design.php'; -$design = new Design(); +$design = new \Magento\PatchChecker\Design(); +$mqpVersion = new \Magento\PatchChecker\Patch\MQP\Version(); require_once 'design/templates/index.phtml';