diff --git a/build/deleted_file_check.php b/build/deleted_file_check.php index e61054d4f0c10..90a06cafcd4b1 100644 --- a/build/deleted_file_check.php +++ b/build/deleted_file_check.php @@ -1,16 +1,20 @@ :' . PHP_TAB . 'Starting commit reference (branch/tag)' . PHP_EOL; - echo PHP_TAB . '--to :' . PHP_TAB . 'Ending commit reference (branch/tag) [optional]' . PHP_EOL; + echo PHP_TAB . '--from :' . PHP_TAB . 'Path to starting version' . PHP_EOL; + echo PHP_TAB . '--to :' . PHP_TAB . 'Path to ending version [optional]' . PHP_EOL; + echo PHP_EOL; + echo ' can be either of the following:' . PHP_EOL; + echo PHP_TAB . '- Path to a full package Zip file.' . PHP_EOL; + echo PHP_TAB . '- Path to a directory where a full package Zip file has been extracted to.' . PHP_EOL; + echo PHP_EOL; + echo 'If the "to" parameter is not specified, file "build/tmp/packages/*Full_Package.zip"' . PHP_EOL; + echo 'is used if it exists from a previous run of the build script.' . PHP_EOL; echo PHP_EOL; } @@ -38,120 +51,211 @@ function usage($command) $options = getopt('', array('from:', 'to::')); -// We need the from reference, otherwise we're doomed to fail +// We need the "from" parameter, otherwise we're doomed to fail if (empty($options['from'])) { echo PHP_EOL; - echo 'Missing starting directory' . PHP_EOL; + echo 'Missing "from" parameter' . PHP_EOL; usage($argv[0]); exit(1); } -// Missing the to reference? No problem, grab the current HEAD +// If the "to" parameter is not specified, use the default if (empty($options['to'])) +{ + // Import the version class to get the version information + define('JPATH_PLATFORM', 1); + require_once dirname(__DIR__) . '/libraries/src/Version.php'; + + $fullVersion = (new Version)->getShortVersion(); + $packageStability = str_replace(' ', '_', Version::DEV_STATUS); + $packageFile = __DIR__ . '/tmp/packages/Joomla_' . $fullVersion . '-' . $packageStability . '-Full_Package.zip'; + + if (is_file($packageFile)) + { + $options['to'] = $packageFile; + } + else + { + echo PHP_EOL; + echo 'Missing "to" parameter and no zip file "' . $packageFile . '" found.' . PHP_EOL; + + usage($argv[0]); + + exit(1); + } +} + +// Check from and to if folder or zip file +if (!is_dir($options['from']) && !(is_file($options['from']) && substr(strtolower($options['from']), -4) === '.zip')) { echo PHP_EOL; - echo 'Missing ending directory' . PHP_EOL; + echo 'The "from" parameter is neither a directory nor a zip file' . PHP_EOL; - usage($argv[0]); + exit(1); +} + +if (!is_dir($options['to']) && !(is_file($options['to']) && substr(strtolower($options['to']), -4) === '.zip')) +{ + echo PHP_EOL; + echo 'The "to" parameter is neither a directory nor a zip file' . PHP_EOL; exit(1); } -// Directories to skip for the check (needs to include anything from J3 we want to keep) -$previousReleaseExclude = [ - $options['from'] . '/administrator/components/com_search', - $options['from'] . '/components/com_search', - $options['from'] . '/images/sampledata', - $options['from'] . '/installation', - $options['from'] . '/media/plg_quickicon_eos310', - $options['from'] . '/media/system/images', - $options['from'] . '/modules/mod_search', - $options['from'] . '/plugins/fields/repeatable', - $options['from'] . '/plugins/quickicon/eos310', - $options['from'] . '/plugins/search', +// Directories to skip for the check +$excludedFolders = [ + 'administrator/components/com_search', + 'components/com_search', + 'images/sampledata', + 'installation', + 'media/plg_quickicon_eos310', + 'media/system/images', + 'modules/mod_search', + 'plugins/fields/repeatable', + 'plugins/quickicon/eos310', + 'plugins/search', ]; /** - * @param SplFileInfo $file The file being checked - * @param mixed $key ? - * @param RecursiveCallbackFilterIterator $iterator The iterator being processed + * @param string $folderPath Path to the folder with the extracted full package + * @param array $excludeFolders Excluded folders * - * @return bool True if you need to recurse or if the item is acceptable + * @return stdClass An object with arrays "files" and "folders" */ -$previousReleaseFilter = function ($file, $key, $iterator) use ($previousReleaseExclude) { - if ($iterator->hasChildren() && !in_array($file->getPathname(), $previousReleaseExclude)) +function readFolder($folderPath, $excludeFolders): stdClass +{ + $return = new stdClass; + + $return->files = []; + $return->folders = []; + + $skipFolders = []; + + foreach ($excludeFolders as $excludeFolder) { - return true; + $skipFolders[] = $folderPath . '/' . $excludeFolder; } - return $file->isFile(); -}; + /** + * @param SplFileInfo $file The file being checked + * @param mixed $key ? + * @param RecursiveCallbackFilterIterator $iterator The iterator being processed + * + * @return bool True if you need to recurse or if the item is acceptable + */ + $releaseFilter = function ($file, $key, $iterator) use ($skipFolders) { + if ($iterator->hasChildren() && !in_array($file->getPathname(), $skipFolders)) + { + return true; + } -// Directories to skip for the check -$newReleaseExclude = [ - $options['to'] . '/installation' -]; + return $file->isFile(); + }; + + $releaseDirIterator = new RecursiveDirectoryIterator($folderPath, RecursiveDirectoryIterator::SKIP_DOTS); + $releaseIterator = new RecursiveIteratorIterator( + new RecursiveCallbackFilterIterator($releaseDirIterator, $releaseFilter), + RecursiveIteratorIterator::SELF_FIRST + ); + + foreach ($releaseIterator as $info) + { + if ($info->isDir()) + { + $return->folders[] = "'" . str_replace($folderPath, '', $info->getPathname()) . "',"; + continue; + } + + $return->files[] = "'" . str_replace($folderPath, '', $info->getPathname()) . "',"; + } + + return $return; +} /** - * @param SplFileInfo $file The file being checked - * @param mixed $key ? - * @param RecursiveCallbackFilterIterator $iterator The iterator being processed + * @param string $filePath Path to the full package zip file + * @param array $excludeFolders Excluded folders * - * @return bool True if you need to recurse or if the item is acceptable + * @return stdClass An object with arrays "files" and "folders" */ -$newReleaseFilter = function ($file, $key, $iterator) use ($newReleaseExclude) { - if ($iterator->hasChildren() && !in_array($file->getPathname(), $newReleaseExclude)) - { - return true; - } +function readZipFile($filePath, $excludeFolders): stdClass +{ + $return = new stdClass; - return $file->isFile(); -}; + $return->files = []; + $return->folders = []; -$previousReleaseDirIterator = new RecursiveDirectoryIterator($options['from'], RecursiveDirectoryIterator::SKIP_DOTS); -$previousReleaseIterator = new RecursiveIteratorIterator( - new RecursiveCallbackFilterIterator($previousReleaseDirIterator, $previousReleaseFilter), - RecursiveIteratorIterator::SELF_FIRST -); -$previousReleaseFiles = []; -$previousReleaseFolders = []; + $zipArchive = new ZipArchive(); -foreach ($previousReleaseIterator as $info) -{ - if ($info->isDir()) + if ($zipArchive->open($filePath) !== true) { - $previousReleaseFolders[] = "'" . str_replace($options['from'], '', $info->getPathname()) . "',"; - continue; + echo PHP_EOL; + echo 'Could not open zip archive "' . $filePath . '".' . PHP_EOL; + + exit(1); } - $previousReleaseFiles[] = "'" . str_replace($options['from'], '', $info->getPathname()) . "',"; -} + $excludeRegexp = '/^('; -$newReleaseDirIterator = new RecursiveDirectoryIterator($options['to'], RecursiveDirectoryIterator::SKIP_DOTS); -$newReleaseIterator = new RecursiveIteratorIterator( - new RecursiveCallbackFilterIterator($newReleaseDirIterator, $newReleaseFilter), - RecursiveIteratorIterator::SELF_FIRST -); -$newReleaseFiles = []; -$newReleaseFolders = []; + foreach ($excludeFolders as $excludeFolder) + { + $excludeRegexp .= preg_quote($excludeFolder, '/') . '|'; + } -foreach ($newReleaseIterator as $info) -{ - if ($info->isDir()) + $excludeRegexp = rtrim($excludeRegexp, '|') . ')\/.*/'; + + for ($i = 0; $i < $zipArchive->numFiles; $i++) { - $newReleaseFolders[] = "'" . str_replace($options['to'], '', $info->getPathname()) . "',"; - continue; + $stat = $zipArchive->statIndex($i); + + $name = $stat['name']; + + if (preg_match($excludeRegexp, $name) === 1) + { + continue; + } + + if (substr($name, -1) === '/') + { + $return->folders[] = "'/" . rtrim($name, '/') . "',"; + } + else + { + $return->files[] = "'/" . $name . "',"; + } } - $newReleaseFiles[] = "'" . str_replace($options['to'], '', $info->getPathname()) . "',"; + $zipArchive->close(); + + return $return; } -$filesDifference = array_diff($previousReleaseFiles, $newReleaseFiles); +// Read files and folders lists from folders or zip files +if (is_dir($options['from'])) +{ + $previousReleaseFilesFolders = readFolder($options['from'], $excludedFolders); +} +else +{ + $previousReleaseFilesFolders = readZipFile($options['from'], $excludedFolders); +} -$foldersDifference = array_diff($previousReleaseFolders, $newReleaseFolders); +if (is_dir($options['to'])) +{ + $newReleaseFilesFolders = readFolder($options['to'], $excludedFolders); +} +else +{ + $newReleaseFilesFolders = readZipFile($options['to'], $excludedFolders); +} + +$filesDifferenceAdd = array_diff($newReleaseFilesFolders->files, $previousReleaseFilesFolders->files); +$filesDifferenceDelete = array_diff($previousReleaseFilesFolders->files, $newReleaseFilesFolders->files); +$foldersDifferenceAdd = array_diff($newReleaseFilesFolders->folders, $previousReleaseFilesFolders->folders); +$foldersDifferenceDelete = array_diff($previousReleaseFilesFolders->folders, $newReleaseFilesFolders->folders); // Specific files (e.g. language files) that we want to keep on upgrade $filesToKeep = [ @@ -191,18 +295,18 @@ function usage($command) // Remove folders from the results which we want to keep on upgrade foreach ($foldersToKeep as $folder) { - if (($key = array_search($folder, $foldersDifference)) !== false) { - unset($foldersDifference[$key]); + if (($key = array_search($folder, $foldersDifferenceDelete)) !== false) { + unset($foldersDifferenceDelete[$key]); } } -asort($filesDifference); -rsort($foldersDifference); +asort($filesDifferenceDelete); +rsort($foldersDifferenceDelete); $deletedFiles = []; $renamedFiles = []; -foreach ($filesDifference as $file) +foreach ($filesDifferenceDelete as $file) { // Don't remove any specific files (e.g. language files) that we want to keep on upgrade if (array_search($file, $filesToKeep) !== false) @@ -211,7 +315,7 @@ function usage($command) } // Check for files which might have been renamed only - $matches = preg_grep('/^' . preg_quote($file, '/') . '$/i', $newReleaseFiles); + $matches = preg_grep('/^' . preg_quote($file, '/') . '$/i', $newReleaseFilesFolders->files); if ($matches !== false) { @@ -222,7 +326,7 @@ function usage($command) // File has been renamed only: Add to renamed files list $renamedFiles[] = substr($file, 0, -1) . ' => ' . $match; - // Go on with the next file in $filesDifference + // Go on with the next file in $filesDifferenceDelete continue 2; } } @@ -233,9 +337,60 @@ function usage($command) } // Write the lists to files for later reference -file_put_contents(__DIR__ . '/deleted_files.txt', implode("\n", $deletedFiles)); -file_put_contents(__DIR__ . '/deleted_folders.txt', implode("\n", $foldersDifference)); -file_put_contents(__DIR__ . '/renamed_files.txt', implode("\n", $renamedFiles)); +$addedFilesFile = __DIR__ . '/added_files.txt'; +$addedFoldersFile = __DIR__ . '/added_folders.txt'; +$deletedFilesFile = __DIR__ . '/deleted_files.txt'; +$deletedFoldersFile = __DIR__ . '/deleted_folders.txt'; +$renamedFilesFile = __DIR__ . '/renamed_files.txt'; + +@unlink($addedFilesFile); +@unlink($addedFoldersFile); +@unlink($deletedFilesFile); +@unlink($deletedFoldersFile); +@unlink($renamedFilesFile); + +if (count($filesDifferenceAdd) > 0) +{ + file_put_contents($addedFilesFile, implode("\n", $filesDifferenceAdd)); +} + +if (count($foldersDifferenceAdd) > 0) +{ + file_put_contents($addedFoldersFile, implode("\n", $foldersDifferenceAdd)); +} + +if (count($deletedFiles) > 0) +{ + file_put_contents($deletedFilesFile, implode("\n", $deletedFiles)); +} + +if (count($foldersDifferenceDelete) > 0) +{ + file_put_contents($deletedFoldersFile, implode("\n", $foldersDifferenceDelete)); +} + +if (count($renamedFiles) > 0) +{ + file_put_contents($renamedFilesFile, implode("\n", $renamedFiles)); +} + +echo PHP_EOL; +echo 'There are ' . PHP_EOL; +echo ' - ' . count($filesDifferenceAdd) . ' added files, ' . PHP_EOL; +echo ' - ' . count($foldersDifferenceAdd) . ' added folders, ' . PHP_EOL; +echo ' - ' . count($deletedFiles) . ' deleted files, ' . PHP_EOL; +echo ' - ' . count($foldersDifferenceDelete) . ' deleted folders and ' . PHP_EOL; +echo ' - ' . count($renamedFiles) . ' renamed files' . PHP_EOL; +echo PHP_EOL; +echo 'in comparison' . PHP_EOL; +echo ' from "' . $options['from'] . '"' . PHP_EOL; +echo ' to "' . $options['to'] . '"' . PHP_EOL; +echo PHP_EOL; +echo 'The following folders and their subfolders have been skipped so they were not included in the comparison:' . PHP_EOL; + +foreach ($excludedFolders as $excludedFolder) +{ + echo ' - ' . $excludedFolder . PHP_EOL; +} echo PHP_EOL; -echo 'There are ' . count($deletedFiles) . ' deleted files, ' . count($foldersDifference) . ' deleted folders and ' . count($renamedFiles) . ' renamed files in comparison to "' . $options['from'] . '"' . PHP_EOL;