diff --git a/docs/commands.md b/docs/commands.md index 8b4391d..7eb4941 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -205,7 +205,7 @@ bin/magento mageforge:hyva:compatibility:check [options] **Options**: - `--show-all` / `-a` - Show all modules including compatible ones -- `--third-party-only` / `-t` - Check only third-party modules (exclude Magento_* modules) +- `--third-party-only` / `-t` - Check only third-party modules (exclude Magento\_\* modules) - `--include-vendor` - Include Magento core modules in scan (default: third-party only) - `--detailed` / `-d` - Show detailed file-level issues for incompatible modules @@ -273,7 +273,7 @@ bin/magento mageforge:hyva:compatibility:check --detailed - Critical issues and warnings count - Shows detailed file paths and line numbers with `--detailed` flag - Provides helpful recommendations for resolving issues -- Returns exit code 1 if critical issues are found, 0 otherwise (even if warnings exist) +- Returns exit code 1 if any critical issues are found. If only warnings (and no critical issues) are detected, the command returns exit code 0 so CI/CD pipelines do not fail on warnings alone. **Detected Patterns**: diff --git a/src/Console/Command/Hyva/CompatibilityCheckCommand.php b/src/Console/Command/Hyva/CompatibilityCheckCommand.php index e3f3fc6..d131e77 100644 --- a/src/Console/Command/Hyva/CompatibilityCheckCommand.php +++ b/src/Console/Command/Hyva/CompatibilityCheckCommand.php @@ -56,7 +56,7 @@ protected function configure(): void self::OPTION_INCLUDE_VENDOR, null, InputOption::VALUE_NONE, - 'Include vendor modules in scan (default: excluded, but included with --third-party-only)' + 'Include Magento core modules (default: third-party modules only)' ) ->addOption( self::OPTION_DETAILED, @@ -96,7 +96,8 @@ private function runInteractiveMode(InputInterface $input, OutputInterface $outp label: 'Select scan options', options: [ 'show-all' => 'Show all modules including compatible ones', - 'include-vendor' => 'Include vendor modules (default: excluded)', + 'incompatible-only' => 'Show only incompatible modules (default behavior)', + 'include-vendor' => 'Include Magento core modules (default: third-party only)', 'detailed' => 'Show detailed file-level issues with line numbers', ], default: [], @@ -110,6 +111,7 @@ private function runInteractiveMode(InputInterface $input, OutputInterface $outp // Apply selected options to input $showAll = in_array('show-all', $selectedOptions); + $incompatibleOnly = in_array('incompatible-only', $selectedOptions); $includeVendor = in_array('include-vendor', $selectedOptions); $detailed = in_array('detailed', $selectedOptions); $thirdPartyOnly = false; // Not needed in interactive mode @@ -119,11 +121,13 @@ private function runInteractiveMode(InputInterface $input, OutputInterface $outp $config = []; if ($showAll) { $config[] = 'Show all modules'; + } elseif ($incompatibleOnly) { + $config[] = 'Show incompatible only'; } else { $config[] = 'Show modules with issues'; } if ($includeVendor) { - $config[] = 'Include vendor modules'; + $config[] = 'Include Magento core'; } else { $config[] = 'Third-party modules only'; } @@ -133,8 +137,8 @@ private function runInteractiveMode(InputInterface $input, OutputInterface $outp $this->io->comment('Configuration: ' . implode(', ', $config)); $this->io->newLine(); - // Run scan with selected options - return $this->runScan($showAll, $thirdPartyOnly, $includeVendor, $detailed, false, $output); + // Run scan with selected options (pass incompatibleOnly flag) + return $this->runScan($showAll, $thirdPartyOnly, $includeVendor, $detailed, $incompatibleOnly, $output); } catch (\Exception $e) { $this->resetPromptEnvironment(); $this->io->error('Interactive mode failed: ' . $e->getMessage()); @@ -171,12 +175,12 @@ private function runScan( OutputInterface $output ): int { - // Determine filter logic for vendor and third-party modules: - // - By default (no flags): scan third-party modules excluding those in vendor/ - // - With --include-vendor: include vendor modules in the scan - // - With --third-party-only: explicitly scan only third-party modules - $scanThirdPartyOnly = $thirdPartyOnly || (!$includeVendor && !$thirdPartyOnly); - $excludeVendor = !$includeVendor; + // Determine filter logic: + // - thirdPartyOnly: Only scan non-Magento_* modules (default behavior) + // - includeVendor: Also scan Magento_* core modules + // - excludeVendor: Whether to exclude vendor/ directory (always false for now) + $scanThirdPartyOnly = !$includeVendor; + $excludeVendor = false; // Run the compatibility check $results = $this->compatibilityChecker->check( @@ -187,17 +191,14 @@ private function runScan( $excludeVendor ); - // Determine display mode based on flags - // If incompatibleOnly is set, only show modules with issues - // If showAll is set, show everything - // Otherwise, show default (incompatible only) - $displayShowAll = $showAll; - if ($incompatibleOnly && !$showAll) { - $displayShowAll = false; // Only show incompatible - } + // Determine display mode: + // showAll = show all modules including compatible ones + // incompatibleOnly = show only modules with critical issues + // default = show modules with any issues (critical or warnings) + $displayShowAll = $showAll && !$incompatibleOnly; // Display results - $this->displayResults($results, $displayShowAll || $detailed); + $this->displayResults($results, $displayShowAll); // Display detailed issues if requested if ($detailed && $results['hasIncompatibilities']) { @@ -364,25 +365,9 @@ private function isInteractiveTerminal(OutputInterface $output): bool } } - // Additional check: detect if running in a proper TTY using safer methods - if (\function_exists('stream_isatty') && \defined('STDIN')) { - try { - return \stream_isatty(\STDIN); - } catch (\Throwable $e) { - // Fall through to next check - } - } - - if (\function_exists('posix_isatty') && \defined('STDIN')) { - try { - return \posix_isatty(\STDIN); - } catch (\Throwable $e) { - // Fall through to default - } - } - - // Conservative default if no TTY-detection functions are available - return false; + // Additional check: try to detect if running in a proper TTY + $sttyOutput = shell_exec('stty -g 2>/dev/null'); + return !empty($sttyOutput); } /** @@ -425,11 +410,7 @@ private function resetPromptEnvironment(): void */ private function removeSecureEnvironmentValue(string $name): void { - // Remove the specific variable from our secure storage unset($this->secureEnvStorage[$name]); - - // Clear the static cache to force refresh on next access - $this->clearEnvironmentCache(); } /** @@ -461,19 +442,8 @@ private function getServerVar(string $name): ?string private function setEnvVar(string $name, string $value): void { $this->secureEnvStorage[$name] = $value; - $_SERVER[$name] = $value; + putenv("$name=$value"); } - /** - * Clear environment cache - */ - private function clearEnvironmentCache(): void - { - // Force refresh on next access by clearing our storage - $this->secureEnvStorage = array_filter( - $this->secureEnvStorage, - fn($key) => in_array($key, ['COLUMNS', 'LINES', 'TERM']), - ARRAY_FILTER_USE_KEY - ); - } + } diff --git a/src/Service/Hyva/IncompatibilityDetector.php b/src/Service/Hyva/IncompatibilityDetector.php index b3141b2..038e111 100644 --- a/src/Service/Hyva/IncompatibilityDetector.php +++ b/src/Service/Hyva/IncompatibilityDetector.php @@ -43,7 +43,7 @@ class IncompatibilityDetector 'severity' => self::SEVERITY_WARNING, ], [ - 'pattern' => '/(?:define|require)\s*\(\s*\[[^\]]*["\']mage\/[^"\']*["\']\s*[^\]]*\]/', + 'pattern' => '/(?:define|require)\s*\(\s*\[[^\]]*["\']mage\/[^"\']*["\']/', 'description' => 'Magento RequireJS module reference', 'severity' => self::SEVERITY_CRITICAL, ], @@ -65,7 +65,7 @@ class IncompatibilityDetector 'severity' => self::SEVERITY_CRITICAL, ], [ - 'pattern' => '/]*\bremove\s*=\s*"true"[^>]*>/s', + 'pattern' => '//', 'description' => 'Block removal (review for Hyvä compatibility)', 'severity' => self::SEVERITY_WARNING, ], @@ -82,7 +82,7 @@ class IncompatibilityDetector 'severity' => self::SEVERITY_CRITICAL, ], [ - 'pattern' => '/\$\([^)]*\)\s*\.(on|click|ready|change|keyup|keydown|submit|ajax|each|css|hide|show|addClass|removeClass|toggleClass|append|prepend|html|text|val|attr|prop|data|trigger|find|parent|children)\s*\(/', + 'pattern' => '/\$\(.*\)\..*\(/', 'description' => 'jQuery DOM manipulation', 'severity' => self::SEVERITY_WARNING, ], diff --git a/src/Service/Hyva/ModuleScanner.php b/src/Service/Hyva/ModuleScanner.php index c695eb3..9a956a4 100644 --- a/src/Service/Hyva/ModuleScanner.php +++ b/src/Service/Hyva/ModuleScanner.php @@ -107,7 +107,31 @@ private function findRelevantFiles(string $directory): array } /** - * Check if module has Hyvä compatibility package + * Check if module has Hyvä compatibility package based on composer data + * + * @param array $composerData Parsed composer.json data + */ + private function isHyvaCompatibilityPackage(array $composerData): bool + { + // Check if this IS a Hyvä compatibility package + $packageName = $composerData['name'] ?? ''; + if (str_starts_with($packageName, 'hyva-themes/') && str_contains($packageName, '-compat')) { + return true; + } + + // Check dependencies for Hyvä packages + $requires = $composerData['require'] ?? []; + foreach ($requires as $package => $version) { + if (str_starts_with($package, 'hyva-themes/')) { + return true; + } + } + + return false; + } + + /** + * Check if module has Hyvä compatibility package (public wrapper) */ public function hasHyvaCompatibilityPackage(string $modulePath): bool { @@ -125,21 +149,7 @@ public function hasHyvaCompatibilityPackage(string $modulePath): bool return false; } - // Check if this IS a Hyvä compatibility package - $packageName = $composerData['name'] ?? ''; - if (str_starts_with($packageName, 'hyva-themes/') && str_contains($packageName, '-compat')) { - return true; - } - - // Check dependencies for Hyvä packages - $requires = $composerData['require'] ?? []; - foreach ($requires as $package => $version) { - if (str_starts_with($package, 'hyva-themes/')) { - return true; - } - } - - return false; + return $this->isHyvaCompatibilityPackage($composerData); } catch (\Exception $e) { // Log error when debugging to help identify JSON or file read issues if (getenv('MAGEFORGE_DEBUG')) { diff --git a/src/etc/di.xml b/src/etc/di.xml index beb8235..67293d1 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -3,54 +3,37 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd" > - - - - OpenForgeProject\MageForge\Console\Command\System\VersionCommand - OpenForgeProject\MageForge\Console\Command\System\CheckCommand - OpenForgeProject\MageForge\Console\Command\Theme\ListCommand - OpenForgeProject\MageForge\Console\Command\Theme\BuildCommand - OpenForgeProject\MageForge\Console\Command\Theme\WatchCommand - OpenForgeProject\MageForge\Console\Command\Static\CleanCommand - OpenForgeProject\MageForge\Console\Command\Hyva\CompatibilityCheckCommand - - - + + + + + + OpenForgeProject\MageForge\Console\Command\System\VersionCommand + + OpenForgeProject\MageForge\Console\Command\System\CheckCommand + + OpenForgeProject\MageForge\Console\Command\Theme\ListCommand + + OpenForgeProject\MageForge\Console\Command\Theme\BuildCommand + + OpenForgeProject\MageForge\Console\Command\Theme\WatchCommand + + OpenForgeProject\MageForge\Console\Command\Static\CleanCommand + + OpenForgeProject\MageForge\Console\Command\Hyva\CompatibilityCheckCommand + + + - - + + OpenForgeProject\MageForge\Service\ThemeBuilder\HyvaThemes\Builder - + OpenForgeProject\MageForge\Service\ThemeBuilder\MagentoStandard\Builder - + OpenForgeProject\MageForge\Service\ThemeBuilder\TailwindCSS\Builder