11<?php
22
33/**
4- * This file is used to build the list of deleted files between two reference points.
4+ * This file is used to build the lists of deleted files, deleted folders and
5+ * renamed files between two Joomla versions.
56 *
67 * This script requires one parameter:
78 *
8- * --from - The git commit reference to use as the starting point for the comparison.
9+ * --from - Full package zip file or folder with unpacked full package of the
10+ * starting point for the comparison, i.e. the older version.
911 *
1012 * This script has one additional optional parameter:
1113 *
12- * --to - The git commit reference to use as the ending point for the comparison.
14+ * --to - Full package zip file or folder with unpacked full package of the
15+ * ending point for the comparison, i.e. the newer version.
1316 *
14- * The reference parameters may be any valid identifier (i.e. a branch, tag, or commit SHA)
17+ * If the "to" parameter is not given, the full package zip from a previous
18+ * run of the build script is used, if present.
1519 *
1620 * @package Joomla.Build
1721 *
1822 * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
1923 * @license GNU General Public License version 2 or later; see LICENSE.txt
2024 */
2125
26+ use Joomla \CMS \Version ;
27+
2228/*
2329 * Constants
2430 */
@@ -28,8 +34,15 @@ function usage($command)
2834{
2935 echo PHP_EOL ;
3036 echo 'Usage: php ' . $ command . ' [options] ' . PHP_EOL ;
31- echo PHP_TAB . '--from <ref>: ' . PHP_TAB . 'Starting commit reference (branch/tag) ' . PHP_EOL ;
32- echo PHP_TAB . '--to <ref>: ' . PHP_TAB . 'Ending commit reference (branch/tag) [optional] ' . PHP_EOL ;
37+ echo PHP_TAB . '--from <path>: ' . PHP_TAB . 'Path to starting version ' . PHP_EOL ;
38+ echo PHP_TAB . '--to <path>: ' . PHP_TAB . 'Path to ending version [optional] ' . PHP_EOL ;
39+ echo PHP_EOL ;
40+ echo '<path> can be either of the following: ' . PHP_EOL ;
41+ echo PHP_TAB . '- Path to a full package Zip file. ' . PHP_EOL ;
42+ echo PHP_TAB . '- Path to a directory where a full package Zip file has been extracted to. ' . PHP_EOL ;
43+ echo PHP_EOL ;
44+ echo 'If the "to" parameter is not specified, file "build/tmp/packages/*Full_Package.zip" ' . PHP_EOL ;
45+ echo 'is used if it exists from a previous run of the build script. ' . PHP_EOL ;
3346 echo PHP_EOL ;
3447}
3548
@@ -39,104 +52,179 @@ function usage($command)
3952
4053$ options = getopt ('' , ['from: ' , 'to:: ' ]);
4154
42- // We need the from reference , otherwise we're doomed to fail
55+ // We need the " from" parameter , otherwise we're doomed to fail
4356if (empty ($ options ['from ' ])) {
4457 echo PHP_EOL ;
45- echo 'Missing starting directory ' . PHP_EOL ;
58+ echo 'Missing "from" parameter ' . PHP_EOL ;
4659
4760 usage ($ argv [0 ]);
4861
4962 exit (1 );
5063}
5164
52- // Missing the to reference? No problem, grab the current HEAD
65+ // If the "to" parameter is not specified, use the default
5366if (empty ($ options ['to ' ])) {
67+ // Import the version class to get the version information
68+ \define ('JPATH_PLATFORM ' , 1 );
69+ require_once \dirname (__DIR__ ) . '/libraries/src/Version.php ' ;
70+
71+ $ fullVersion = (new Version ())->getShortVersion ();
72+ $ packageStability = str_replace (' ' , '_ ' , Version::DEV_STATUS );
73+ $ packageFile = __DIR__ . '/tmp/packages/Joomla_ ' . $ fullVersion . '- ' . $ packageStability . '-Full_Package.zip ' ;
74+
75+ if (is_file ($ packageFile )) {
76+ $ options ['to ' ] = $ packageFile ;
77+ } else {
78+ echo PHP_EOL ;
79+ echo 'Missing "to" parameter and no zip file " ' . $ packageFile . '" found. ' . PHP_EOL ;
80+
81+ usage ($ argv [0 ]);
82+
83+ exit (1 );
84+ }
85+ }
86+
87+ // Check from and to if folder or zip file
88+ if (!is_dir ($ options ['from ' ]) && !(is_file ($ options ['from ' ]) && substr (strtolower ($ options ['from ' ]), -4 ) === '.zip ' )) {
5489 echo PHP_EOL ;
55- echo 'Missing ending directory ' . PHP_EOL ;
90+ echo 'The "from" parameter is neither a directory nor a zip file ' . PHP_EOL ;
5691
57- usage ($ argv [0 ]);
92+ exit (1 );
93+ }
94+
95+ if (!is_dir ($ options ['to ' ]) && !(is_file ($ options ['to ' ]) && substr (strtolower ($ options ['to ' ]), -4 ) === '.zip ' )) {
96+ echo PHP_EOL ;
97+ echo 'The "to" parameter is neither a directory nor a zip file ' . PHP_EOL ;
5898
5999 exit (1 );
60100}
61101
62- // Directories to skip for the check (needs to include anything from previous versions which we want to keep)
63- $ previousReleaseExclude = [
64- $ options [ ' from ' ] . ' / images/sampledata ' ,
65- $ options [ ' from ' ] . ' / installation ' ,
102+ // Directories to skip for the check
103+ $ excludedFolders = [
104+ ' images/sampledata ' ,
105+ ' installation ' ,
66106];
67107
68108/**
69- * @param SplFileInfo $file The file being checked
70- * @param mixed $key ?
71- * @param RecursiveCallbackFilterIterator $iterator The iterator being processed
109+ * @param string $folderPath Path to the folder with the extracted full package
110+ * @param array $excludeFolders Excluded folders
72111 *
73- * @return bool True if you need to recurse or if the item is acceptable
112+ * @return stdClass An object with arrays "files" and "folders"
74113 */
75- $ previousReleaseFilter = function ($ file , $ key , $ iterator ) use ($ previousReleaseExclude ) {
76- if ($ iterator ->hasChildren () && !\in_array ($ file ->getPathname (), $ previousReleaseExclude )) {
77- return true ;
114+ function readFolder ($ folderPath , $ excludeFolders ): stdClass
115+ {
116+ $ return = new stdClass ();
117+
118+ $ return ->files = [];
119+ $ return ->folders = [];
120+
121+ $ skipFolders = [];
122+
123+ foreach ($ excludeFolders as $ excludeFolder ) {
124+ $ skipFolders [] = $ folderPath . '/ ' . $ excludeFolder ;
78125 }
79126
80- return $ file ->isFile ();
81- };
127+ /**
128+ * @param SplFileInfo $file The file being checked
129+ * @param mixed $key ?
130+ * @param RecursiveCallbackFilterIterator $iterator The iterator being processed
131+ *
132+ * @return bool True if you need to recurse or if the item is acceptable
133+ */
134+ $ releaseFilter = function ($ file , $ key , $ iterator ) use ($ skipFolders ) {
135+ if ($ iterator ->hasChildren () && !\in_array ($ file ->getPathname (), $ skipFolders )) {
136+ return true ;
137+ }
82138
83- // Directories to skip for the check
84- $ newReleaseExclude = [
85- $ options ['to ' ] . '/installation ' ,
86- ];
139+ return $ file ->isFile ();
140+ };
141+
142+ $ releaseDirIterator = new RecursiveDirectoryIterator ($ folderPath , RecursiveDirectoryIterator::SKIP_DOTS );
143+ $ releaseIterator = new RecursiveIteratorIterator (
144+ new RecursiveCallbackFilterIterator ($ releaseDirIterator , $ releaseFilter ),
145+ RecursiveIteratorIterator::SELF_FIRST
146+ );
147+
148+ foreach ($ releaseIterator as $ info ) {
149+ if ($ info ->isDir ()) {
150+ $ return ->folders [] = "' " . str_replace ($ folderPath , '' , $ info ->getPathname ()) . "', " ;
151+ continue ;
152+ }
153+
154+ $ return ->files [] = "' " . str_replace ($ folderPath , '' , $ info ->getPathname ()) . "', " ;
155+ }
156+
157+ return $ return ;
158+ }
87159
88160/**
89- * @param SplFileInfo $file The file being checked
90- * @param mixed $key ?
91- * @param RecursiveCallbackFilterIterator $iterator The iterator being processed
161+ * @param string $filePath Path to the full package zip file
162+ * @param array $excludeFolders Excluded folders
92163 *
93- * @return bool True if you need to recurse or if the item is acceptable
164+ * @return stdClass An object with arrays "files" and "folders"
94165 */
95- $ newReleaseFilter = function ($ file , $ key , $ iterator ) use ($ newReleaseExclude ) {
96- if ($ iterator ->hasChildren () && !\in_array ($ file ->getPathname (), $ newReleaseExclude )) {
97- return true ;
98- }
166+ function readZipFile ($ filePath , $ excludeFolders ): stdClass
167+ {
168+ $ return = new stdClass ();
99169
100- return $ file -> isFile () ;
101- } ;
170+ $ return -> files = [] ;
171+ $ return -> folders = [] ;
102172
103- $ previousReleaseDirIterator = new RecursiveDirectoryIterator ($ options ['from ' ], RecursiveDirectoryIterator::SKIP_DOTS );
104- $ previousReleaseIterator = new RecursiveIteratorIterator (
105- new RecursiveCallbackFilterIterator ($ previousReleaseDirIterator , $ previousReleaseFilter ),
106- RecursiveIteratorIterator::SELF_FIRST
107- );
108- $ previousReleaseFiles = [];
109- $ previousReleaseFolders = [];
173+ $ zipArchive = new ZipArchive ();
110174
111- foreach ($ previousReleaseIterator as $ info ) {
112- if ($ info ->isDir ()) {
113- $ previousReleaseFolders [] = "' " . str_replace ($ options ['from ' ], '' , $ info ->getPathname ()) . "', " ;
114- continue ;
175+ if ($ zipArchive ->open ($ filePath ) !== true ) {
176+ echo PHP_EOL ;
177+ echo 'Could not open zip archive " ' . $ filePath . '". ' . PHP_EOL ;
178+
179+ exit (1 );
115180 }
116181
117- $ previousReleaseFiles [] = "' " . str_replace ($ options ['from ' ], '' , $ info ->getPathname ()) . "', " ;
118- }
182+ $ excludeRegexp = '/^( ' ;
119183
120- $ newReleaseDirIterator = new RecursiveDirectoryIterator ($ options ['to ' ], RecursiveDirectoryIterator::SKIP_DOTS );
121- $ newReleaseIterator = new RecursiveIteratorIterator (
122- new RecursiveCallbackFilterIterator ($ newReleaseDirIterator , $ newReleaseFilter ),
123- RecursiveIteratorIterator::SELF_FIRST
124- );
125- $ newReleaseFiles = [];
126- $ newReleaseFolders = [];
184+ foreach ($ excludeFolders as $ excludeFolder ) {
185+ $ excludeRegexp .= preg_quote ($ excludeFolder , '/ ' ) . '| ' ;
186+ }
127187
128- foreach ($ newReleaseIterator as $ info ) {
129- if ($ info ->isDir ()) {
130- $ newReleaseFolders [] = "' " . str_replace ($ options ['to ' ], '' , $ info ->getPathname ()) . "', " ;
131- continue ;
188+ $ excludeRegexp = rtrim ($ excludeRegexp , '| ' ) . ')\/.*/ ' ;
189+
190+ for ($ i = 0 ; $ i < $ zipArchive ->numFiles ; $ i ++) {
191+ $ stat = $ zipArchive ->statIndex ($ i );
192+
193+ $ name = $ stat ['name ' ];
194+
195+ if (preg_match ($ excludeRegexp , $ name ) === 1 ) {
196+ continue ;
197+ }
198+
199+ if (substr ($ name , -1 ) === '/ ' ) {
200+ $ return ->folders [] = "'/ " . rtrim ($ name , '/ ' ) . "', " ;
201+ } else {
202+ $ return ->files [] = "'/ " . $ name . "', " ;
203+ }
132204 }
133205
134- $ newReleaseFiles [] = "' " . str_replace ($ options ['to ' ], '' , $ info ->getPathname ()) . "', " ;
206+ $ zipArchive ->close ();
207+
208+ return $ return ;
209+ }
210+
211+ // Read files and folders lists from folders or zip files
212+ if (is_dir ($ options ['from ' ])) {
213+ $ previousReleaseFilesFolders = readFolder ($ options ['from ' ], $ excludedFolders );
214+ } else {
215+ $ previousReleaseFilesFolders = readZipFile ($ options ['from ' ], $ excludedFolders );
135216}
136217
137- $ filesDifference = array_diff ($ previousReleaseFiles , $ newReleaseFiles );
218+ if (is_dir ($ options ['to ' ])) {
219+ $ newReleaseFilesFolders = readFolder ($ options ['to ' ], $ excludedFolders );
220+ } else {
221+ $ newReleaseFilesFolders = readZipFile ($ options ['to ' ], $ excludedFolders );
222+ }
138223
139- $ foldersDifference = array_diff ($ previousReleaseFolders , $ newReleaseFolders );
224+ $ filesDifferenceAdd = array_diff ($ newReleaseFilesFolders ->files , $ previousReleaseFilesFolders ->files );
225+ $ filesDifferenceDelete = array_diff ($ previousReleaseFilesFolders ->files , $ newReleaseFilesFolders ->files );
226+ $ foldersDifferenceAdd = array_diff ($ newReleaseFilesFolders ->folders , $ previousReleaseFilesFolders ->folders );
227+ $ foldersDifferenceDelete = array_diff ($ previousReleaseFilesFolders ->folders , $ newReleaseFilesFolders ->folders );
140228
141229// Specific files (e.g. language files) that we want to keep on upgrade
142230$ filesToKeep = [
@@ -150,33 +238,33 @@ function usage($command)
150238
151239// Remove folders from the results which we want to keep on upgrade
152240foreach ($ foldersToKeep as $ folder ) {
153- if (($ key = array_search ($ folder , $ foldersDifference )) !== false ) {
154- unset($ foldersDifference [$ key ]);
241+ if (($ key = array_search ($ folder , $ foldersDifferenceDelete )) !== false ) {
242+ unset($ foldersDifferenceDelete [$ key ]);
155243 }
156244}
157245
158- asort ($ filesDifference );
159- rsort ($ foldersDifference );
246+ asort ($ filesDifferenceDelete );
247+ rsort ($ foldersDifferenceDelete );
160248
161249$ deletedFiles = [];
162250$ renamedFiles = [];
163251
164- foreach ($ filesDifference as $ file ) {
252+ foreach ($ filesDifferenceDelete as $ file ) {
165253 // Don't remove any specific files (e.g. language files) that we want to keep on upgrade
166254 if (array_search ($ file , $ filesToKeep ) !== false ) {
167255 continue ;
168256 }
169257
170258 // Check for files which might have been renamed only
171- $ matches = preg_grep ('/^ ' . preg_quote ($ file , '/ ' ) . '$/i ' , $ newReleaseFiles );
259+ $ matches = preg_grep ('/^ ' . preg_quote ($ file , '/ ' ) . '$/i ' , $ newReleaseFilesFolders -> files );
172260
173261 if ($ matches !== false ) {
174262 foreach ($ matches as $ match ) {
175263 if (\dirname ($ match ) === \dirname ($ file ) && strtolower (basename ($ match )) === strtolower (basename ($ file ))) {
176264 // File has been renamed only: Add to renamed files list
177265 $ renamedFiles [] = substr ($ file , 0 , -1 ) . ' => ' . $ match ;
178266
179- // Go on with the next file in $filesDifference
267+ // Go on with the next file in $filesDifferenceDelete
180268 continue 2 ;
181269 }
182270 }
@@ -187,9 +275,54 @@ function usage($command)
187275}
188276
189277// Write the lists to files for later reference
190- file_put_contents (__DIR__ . '/deleted_files.txt ' , implode ("\n" , $ deletedFiles ));
191- file_put_contents (__DIR__ . '/deleted_folders.txt ' , implode ("\n" , $ foldersDifference ));
192- file_put_contents (__DIR__ . '/renamed_files.txt ' , implode ("\n" , $ renamedFiles ));
278+ $ addedFilesFile = __DIR__ . '/added_files.txt ' ;
279+ $ addedFoldersFile = __DIR__ . '/added_folders.txt ' ;
280+ $ deletedFilesFile = __DIR__ . '/deleted_files.txt ' ;
281+ $ deletedFoldersFile = __DIR__ . '/deleted_folders.txt ' ;
282+ $ renamedFilesFile = __DIR__ . '/renamed_files.txt ' ;
283+
284+ @unlink ($ addedFilesFile );
285+ @unlink ($ addedFoldersFile );
286+ @unlink ($ deletedFilesFile );
287+ @unlink ($ deletedFoldersFile );
288+ @unlink ($ renamedFilesFile );
289+
290+ if (\count ($ filesDifferenceAdd ) > 0 ) {
291+ file_put_contents ($ addedFilesFile , implode ("\n" , $ filesDifferenceAdd ));
292+ }
293+
294+ if (\count ($ foldersDifferenceAdd ) > 0 ) {
295+ file_put_contents ($ addedFoldersFile , implode ("\n" , $ foldersDifferenceAdd ));
296+ }
297+
298+ if (\count ($ deletedFiles ) > 0 ) {
299+ file_put_contents ($ deletedFilesFile , implode ("\n" , $ deletedFiles ));
300+ }
301+
302+ if (\count ($ foldersDifferenceDelete ) > 0 ) {
303+ file_put_contents ($ deletedFoldersFile , implode ("\n" , $ foldersDifferenceDelete ));
304+ }
305+
306+ if (\count ($ renamedFiles ) > 0 ) {
307+ file_put_contents ($ renamedFilesFile , implode ("\n" , $ renamedFiles ));
308+ }
309+
310+ echo PHP_EOL ;
311+ echo 'There are ' . PHP_EOL ;
312+ echo ' - ' . \count ($ filesDifferenceAdd ) . ' added files, ' . PHP_EOL ;
313+ echo ' - ' . \count ($ foldersDifferenceAdd ) . ' added folders, ' . PHP_EOL ;
314+ echo ' - ' . \count ($ deletedFiles ) . ' deleted files, ' . PHP_EOL ;
315+ echo ' - ' . \count ($ foldersDifferenceDelete ) . ' deleted folders and ' . PHP_EOL ;
316+ echo ' - ' . \count ($ renamedFiles ) . ' renamed files ' . PHP_EOL ;
317+ echo PHP_EOL ;
318+ echo 'in comparison ' . PHP_EOL ;
319+ echo ' from " ' . $ options ['from ' ] . '" ' . PHP_EOL ;
320+ echo ' to " ' . $ options ['to ' ] . '" ' . PHP_EOL ;
321+ echo PHP_EOL ;
322+ echo 'The following folders and their subfolders have been skipped so they were not included in the comparison: ' . PHP_EOL ;
323+
324+ foreach ($ excludedFolders as $ excludedFolder ) {
325+ echo ' - ' . $ excludedFolder . PHP_EOL ;
326+ }
193327
194328echo PHP_EOL ;
195- echo 'There are ' . \count ($ deletedFiles ) . ' deleted files, ' . \count ($ foldersDifference ) . ' deleted folders and ' . \count ($ renamedFiles ) . ' renamed files in comparison to " ' . $ options ['from ' ] . '" ' . PHP_EOL ;
0 commit comments