001
014
015 package com.liferay.portal.tools;
016
017 import com.liferay.portal.kernel.util.FileUtil;
018 import com.liferay.portal.kernel.util.GetterUtil;
019 import com.liferay.portal.kernel.util.NaturalOrderStringComparator;
020 import com.liferay.portal.kernel.util.OSDetector;
021 import com.liferay.portal.kernel.util.PropertiesUtil;
022 import com.liferay.portal.kernel.util.StringBundler;
023 import com.liferay.portal.kernel.util.StringPool;
024 import com.liferay.portal.kernel.util.StringUtil;
025 import com.liferay.portal.kernel.util.Validator;
026 import com.liferay.portal.util.FileImpl;
027
028 import java.io.File;
029
030 import java.util.ArrayList;
031 import java.util.Arrays;
032 import java.util.List;
033 import java.util.Properties;
034 import java.util.Set;
035 import java.util.TreeSet;
036
037 import org.apache.tools.ant.DirectoryScanner;
038
039
042 public class PluginsSummaryBuilder {
043
044 public static void main(String[] args) {
045 ToolDependencies.wireBasic();
046
047 File pluginsDir = new File(System.getProperty("plugins.dir"));
048
049 new PluginsSummaryBuilder(pluginsDir);
050 }
051
052 public PluginsSummaryBuilder(File pluginsDir) {
053 _pluginsDir = pluginsDir;
054
055 String latestHASH = null;
056
057 try {
058 latestHASH = _getLatestHASH(pluginsDir);
059
060 _createPluginsSummary();
061 }
062 catch (Exception e) {
063 e.printStackTrace();
064 }
065
066 _latestHASH = latestHASH;
067 }
068
069 private void _createPluginsSummary() throws Exception {
070 DirectoryScanner directoryScanner = new DirectoryScanner();
071
072 directoryScanner.setBasedir(_pluginsDir);
073 directoryScanner.setExcludes(
074 new String[] {"**\\tmp\\**", "**\\tools\\**"});
075 directoryScanner.setIncludes(
076 new String[] {"**\\liferay-plugin-package.properties"});
077
078 directoryScanner.scan();
079
080 String[] fileNames = directoryScanner.getIncludedFiles();
081
082 Arrays.sort(fileNames);
083
084 _createPluginsSummary(fileNames);
085 }
086
087 private void _createPluginsSummary(String[] fileNames) throws Exception {
088 StringBundler sb = new StringBundler();
089
090 sb.append("<plugins-summary>\n");
091
092 for (String fileName : fileNames) {
093 fileName = StringUtil.replace(
094 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
095
096 _createPluginSummary(sb, fileName);
097 }
098
099 for (String author : _distinctAuthors) {
100 sb.append("\t<author>");
101 sb.append(author);
102 sb.append("</author>\n");
103 }
104
105 for (String license : _distinctLicenses) {
106 sb.append("\t<license>");
107 sb.append(license);
108 sb.append("</license>\n");
109 }
110
111 sb.append("</plugins-summary>");
112
113 FileUtil.write(_pluginsDir + "/summary.xml", sb.toString());
114 }
115
116 private void _createPluginSummary(StringBundler sb, String fileName)
117 throws Exception {
118
119 String content = FileUtil.read(fileName);
120
121 int x = fileName.indexOf(StringPool.SLASH);
122
123 String type = fileName.substring(0, x);
124
125 if (type.endsWith("s")) {
126 type = type.substring(0, type.length() - 1);
127 }
128
129 x = fileName.indexOf(StringPool.SLASH, x) + 1;
130
131 int y = fileName.indexOf(StringPool.SLASH, x);
132
133 String artifactId = fileName.substring(x, y);
134
135 Properties properties = PropertiesUtil.load(content);
136
137 String name = _readProperty(properties, "name");
138 String tags = _readProperty(properties, "tags");
139 String shortDescription = _readProperty(
140 properties, "short-description");
141 String longDescription = _readProperty(properties, "long-description");
142 String changeLog = _readProperty(properties, "change-log");
143 String pageURL = _readProperty(properties, "page-url");
144 String author = _readProperty(properties, "author");
145 String licenses = _readProperty(properties, "licenses");
146 String liferayVersions = _readProperty(properties, "liferay-versions");
147
148 _distinctAuthors.add(author);
149 _distinctLicenses.add(licenses);
150
151 sb.append("\t<plugin>\n");
152
153 _writeElement(sb, "artifact-id", artifactId, 2);
154 _writeElement(sb, "name", name, 2);
155 _writeElement(sb, "type", type, 2);
156 _writeElement(sb, "tags", tags, 2);
157 _writeElement(sb, "short-description", shortDescription, 2);
158 _writeElement(sb, "long-description", longDescription, 2);
159 _writeElement(sb, "change-log", changeLog, 2);
160 _writeElement(sb, "page-url", pageURL, 2);
161 _writeElement(sb, "author", author, 2);
162 _writeElement(sb, "licenses", licenses, 2);
163 _writeElement(sb, "liferay-versions", liferayVersions, 2);
164
165 sb.append("\t\t<releng>\n");
166 sb.append(_readReleng(fileName, properties));
167 sb.append("\t\t</releng>\n");
168 sb.append("\t</plugin>\n");
169 }
170
171 private Set<String> _extractTicketIds(File pluginDir, String range)
172 throws Exception {
173
174 Set<String> ticketIds = new TreeSet<String>(
175 new NaturalOrderStringComparator()
176 );
177
178 Runtime runtime = Runtime.getRuntime();
179
180 String command = "git log " + range + " .";
181
182 if (OSDetector.isWindows()) {
183 command = "cmd /c " + command;
184 }
185
186 Process process = runtime.exec(command, null, pluginDir);
187
188 String content = StringUtil.read(process.getInputStream());
189
190 content = StringUtil.replace(content, "\n", " ");
191
192 for (String ticketIdPrefix : _TICKET_ID_PREFIXES) {
193 int x = 0;
194
195 while (true) {
196 x = content.indexOf(ticketIdPrefix + "-", x);
197
198 if (x == -1) {
199 break;
200 }
201
202 int y = x + ticketIdPrefix.length() + 1;
203
204 while (true) {
205 if ((y + 1) > content.length()) {
206 break;
207 }
208
209 if (Character.isDigit(content.charAt(y))) {
210 y++;
211 }
212 else {
213 break;
214 }
215 }
216
217 String ticketId = content.substring(x, y);
218
219 ticketIds.add(ticketId);
220
221 x = y;
222 }
223 }
224
225 File buildXmlFile = new File(pluginDir, "build.xml");
226 System.out.println("## read a " + buildXmlFile);
227
228 String buildXmlContent = _fileUtil.read(buildXmlFile);
229
230 int x = buildXmlContent.indexOf("import.shared");
231
232 if (x == -1) {
233 return ticketIds;
234 }
235
236 x = buildXmlContent.indexOf("value=\"", x);
237 x = buildXmlContent.indexOf("\"", x);
238
239 int y = buildXmlContent.indexOf("\" />", x);
240
241 if ((x == -1) || (y == -1)) {
242 return ticketIds;
243 }
244
245 String[] importShared = StringUtil.split(
246 buildXmlContent.substring(x + 1, y));
247
248 if (importShared.length == 0) {
249 return ticketIds;
250 }
251
252 for (String currentImportShared : importShared) {
253 File currentImportSharedDir = new File(
254 pluginDir, "../../shared/" + currentImportShared);
255
256 if (!currentImportSharedDir.exists()) {
257 continue;
258 }
259
260 ticketIds.addAll(_extractTicketIds(currentImportSharedDir, range));
261 }
262
263 return ticketIds;
264 }
265
266 private String _getChangeLogEntry(
267 int changeLogVersion, String range, String ticketIdsString) {
268
269 StringBundler sb = new StringBundler(8);
270
271 if (changeLogVersion > 1) {
272 sb.append("\n\n");
273 }
274
275 sb.append("#\n");
276 sb.append("# Module Incremental Version ");
277 sb.append(changeLogVersion);
278 sb.append("\n#\n");
279 sb.append(range);
280 sb.append("=");
281 sb.append(ticketIdsString);
282
283 return sb.toString();
284 }
285
286 private String _getLatestHASH(File pluginDir) throws Exception {
287 Runtime runtime = Runtime.getRuntime();
288
289 String command = "git rev-parse HEAD";
290
291 if (OSDetector.isWindows()) {
292 command = "cmd /c " + command;
293 }
294
295 Process process = runtime.exec(command, null, pluginDir);
296
297 return StringUtil.read(process.getInputStream());
298 }
299
300 private String _readProperty(Properties properties, String key) {
301 return GetterUtil.getString(properties.getProperty(key));
302 }
303
304 private String _readReleng(
305 String fileName, Properties pluginPackageProperties)
306 throws Exception {
307
308 int x = fileName.indexOf("WEB-INF");
309
310 String relativeWebInfDirName = fileName.substring(0, x + 8);
311
312 String fullWebInfDirName =
313 _pluginsDir + StringPool.SLASH + relativeWebInfDirName;
314
315 String relengPropertiesFileName =
316 fullWebInfDirName + "liferay-releng.properties";
317
318 Properties relengProperties = null;
319
320 if (FileUtil.exists(relengPropertiesFileName)) {
321 String relengPropertiesContent = FileUtil.read(
322 relengPropertiesFileName);
323
324 relengProperties = PropertiesUtil.load(relengPropertiesContent);
325 }
326 else {
327 relengProperties = new Properties();
328 }
329
330 String relengPropertiesContent = _updateRelengPropertiesFile(
331 relengPropertiesFileName, relengProperties);
332
333 relengProperties = PropertiesUtil.load(relengPropertiesContent);
334
335 StringBundler sb = new StringBundler();
336
337 _writeElement(sb, "bundle", relengProperties, 3);
338 _writeElement(sb, "category", relengProperties, 3);
339 _writeElement(sb, "demo-url", relengProperties, 3);
340 _writeElement(sb, "dependent-apps", relengProperties, 3);
341
342 if (FileUtil.exists(fullWebInfDirName + "releng/icons/90x90.png")) {
343 _writeElement(
344 sb, "icon", relativeWebInfDirName + "releng/icons/90x90.png",
345 3);
346 }
347
348 _writeElement(sb, "labs", relengProperties, 3);
349 _writeElement(sb, "marketplace", relengProperties, 3);
350 _writeElement(sb, "public", relengProperties, 3);
351
352 String fullScreenshotsDirName =
353 fullWebInfDirName + "releng/screenshots/";
354 String relativeScreenshotsDirName =
355 relativeWebInfDirName + "releng/screenshots/";
356
357 if (FileUtil.exists(fullScreenshotsDirName)) {
358 String[] screenshotsFileNames = FileUtil.listFiles(
359 fullScreenshotsDirName);
360
361 Arrays.sort(screenshotsFileNames);
362
363 for (String screenshotsFileName : screenshotsFileNames) {
364 if (screenshotsFileName.equals("Thumbs.db") ||
365 screenshotsFileName.endsWith(".png")) {
366
367 FileUtil.delete(
368 fullScreenshotsDirName + screenshotsFileName);
369 }
370
371 if (!screenshotsFileName.endsWith(".jpg")) {
372 continue;
373 }
374
375 _writeElement(
376 sb, "screenshot",
377 relativeScreenshotsDirName + screenshotsFileName, 3);
378 }
379 }
380
381 _writeElement(sb, "support-url", relengProperties, 3);
382 _writeElement(sb, "supported", relengProperties, 3);
383
384 File relengChangeLogFile = new File(
385 fullWebInfDirName + "liferay-releng.changelog");
386
387 if (GetterUtil.getBoolean(
388 relengProperties.getProperty("marketplace"))) {
389
390 _updateRelengChangeLogFile(
391 pluginPackageProperties, relengChangeLogFile, relengProperties);
392 }
393 else {
394 relengChangeLogFile.delete();
395 }
396
397 return sb.toString();
398 }
399
400 private void _updateRelengChangeLogFile(
401 Properties pluginPackageProperties, File relengChangeLogFile,
402 Properties relengProperties)
403 throws Exception {
404
405 StringBundler sb = new StringBundler();
406
407 int changeLogVersion = 0;
408
409 int moduleIncrementalVersion = GetterUtil.getInteger(
410 pluginPackageProperties.getProperty("module-incremental-version"));
411
412 if (!relengChangeLogFile.exists()) {
413 FileUtil.write(relengChangeLogFile, "TEMP=");
414 }
415
416 String relengChangeLogContent = FileUtil.read(relengChangeLogFile);
417
418 List<String> relengChangeLogEntries = new ArrayList<>();
419
420 String[] relengChangeLogEntriesArray = StringUtil.split(
421 relengChangeLogContent, "\n");
422
423 for (int i = 0; i < relengChangeLogEntriesArray.length; i++) {
424 String relengChangeLogEntry = relengChangeLogEntriesArray[i];
425
426 if (Validator.isNull(relengChangeLogEntry) ||
427 relengChangeLogEntry.startsWith("#")) {
428
429 continue;
430 }
431
432 relengChangeLogEntries.add(relengChangeLogEntry);
433
434 if (((i + 1) == relengChangeLogEntriesArray.length) &&
435 !relengChangeLogEntry.contains("HEAD=") &&
436 !relengChangeLogEntry.contains("TEMP=") &&
437 !relengChangeLogEntry.contains(_latestHASH) &&
438 !relengChangeLogEntries.isEmpty()) {
439
440 int x = relengChangeLogEntry.indexOf("..");
441 int y = relengChangeLogEntry.indexOf("=", x);
442
443 String range =
444 relengChangeLogEntry.substring(x + 2, y) + "^.." +
445 _latestHASH;
446
447 relengChangeLogEntries.add(range);
448
449 continue;
450 }
451 }
452
453 File webInfDir = relengChangeLogFile.getParentFile();
454
455 File docrootDir = webInfDir.getParentFile();
456
457 File pluginDir = docrootDir.getParentFile();
458
459 for (int i = 0; i < relengChangeLogEntries.size(); i++) {
460 String relengChangeLogEntry = relengChangeLogEntries.get(i);
461
462 String[] relengChangeLogEntryParts = StringUtil.split(
463 relengChangeLogEntry, "=");
464
465 String range = relengChangeLogEntryParts[0];
466
467 if (range.equals("TEMP")) {
468 changeLogVersion++;
469
470 sb.append(
471 _getChangeLogEntry(
472 changeLogVersion, range, StringPool.BLANK));
473
474 break;
475 }
476
477 Set<String> ticketIds = _extractTicketIds(pluginDir, range);
478
479 if (range.endsWith("^.." + _latestHASH) && ticketIds.isEmpty() &&
480 (relengChangeLogEntries.size() > 1)) {
481
482 continue;
483 }
484
485 if (ticketIds.isEmpty()) {
486 System.out.println(
487 pluginDir + " does not have changes for range " + range);
488 }
489
490 String[] dependentApps = StringUtil.split(
491 relengProperties.getProperty("dependent-apps"));
492
493 for (String dependentApp : dependentApps) {
494 dependentApp = dependentApp.trim();
495
496 if (dependentApp.equals("resources-impoter-web")) {
497 continue;
498 }
499
500 String dependentAppDirName = null;
501
502 if (dependentApp.endsWith("-hook")) {
503 dependentAppDirName = "hooks";
504 }
505 else if (dependentApp.endsWith("-layouttpl")) {
506 dependentAppDirName = "layouttpl";
507 }
508 else if (dependentApp.endsWith("-portlet")) {
509 dependentAppDirName = "portlets";
510 }
511 else if (dependentApp.endsWith("-theme")) {
512 dependentAppDirName = "themes";
513 }
514 else if (dependentApp.endsWith("-web")) {
515 dependentAppDirName = "webs";
516 }
517
518 File dependentAppDir = new File(
519 _pluginsDir, dependentAppDirName + "/" + dependentApp);
520
521 if (!dependentAppDir.exists()) {
522 throw new RuntimeException(
523 dependentAppDir + " does not exist");
524 }
525
526 ticketIds.addAll(_extractTicketIds(dependentAppDir, range));
527 }
528
529 String ticketIdsString = StringUtil.merge(
530 ticketIds.toArray(new String[ticketIds.size()]), " ");
531
532 changeLogVersion++;
533
534 sb.append(
535 _getChangeLogEntry(changeLogVersion, range, ticketIdsString));
536 }
537
538 File pluginPackagePropertiesFile = new File(
539 webInfDir, "liferay-plugin-package.properties");
540
541 String pluginPackagePropertiesContent = FileUtil.read(
542 pluginPackagePropertiesFile);
543
544 if (!pluginPackagePropertiesContent.contains("long-description")) {
545 int x = pluginPackagePropertiesContent.indexOf("change-log=");
546
547 pluginPackagePropertiesContent =
548 pluginPackagePropertiesContent.substring(0, x) +
549 "long-description=\n" +
550 pluginPackagePropertiesContent.substring(x);
551 }
552
553 if (moduleIncrementalVersion != changeLogVersion) {
554 pluginPackagePropertiesContent = StringUtil.replace(
555 pluginPackagePropertiesContent,
556 "module-incremental-version=" + moduleIncrementalVersion,
557 "module-incremental-version=" + changeLogVersion);
558 }
559
560 FileUtil.write(
561 pluginPackagePropertiesFile, pluginPackagePropertiesContent);
562
563 FileUtil.write(relengChangeLogFile, sb.toString());
564
565 File relengChangeLogMD5File = new File(
566 webInfDir, "liferay-releng.changelog.md5");
567
568 String md5Checksum = FileUtil.getMD5Checksum(relengChangeLogFile);
569
570 FileUtil.write(relengChangeLogMD5File, md5Checksum);
571 }
572
573 private String _updateRelengPropertiesFile(
574 String relengPropertiesFileName, Properties relengProperties)
575 throws Exception {
576
577 StringBundler sb = new StringBundler();
578
579 _writeProperty(sb, relengProperties, "bundle", "false");
580 _writeProperty(sb, relengProperties, "category", "");
581 _writeProperty(sb, relengProperties, "demo-url", "");
582 _writeProperty(sb, relengProperties, "dependent-apps", "");
583 _writeProperty(sb, relengProperties, "labs", "true");
584 _writeProperty(sb, relengProperties, "marketplace", "false");
585 _writeProperty(sb, relengProperties, "public", "true");
586 _writeProperty(sb, relengProperties, "support-url", "");
587 _writeProperty(sb, relengProperties, "supported", "false");
588
589 String relengPropertiesContent = sb.toString();
590
591 FileUtil.write(relengPropertiesFileName, relengPropertiesContent);
592
593 return relengPropertiesContent;
594 }
595
596 private void _writeElement(
597 StringBundler sb, String name, Properties properties, int tabsCount) {
598
599 _writeElement(sb, name, _readProperty(properties, name), tabsCount);
600 }
601
602 private void _writeElement(
603 StringBundler sb, String name, String value, int tabsCount) {
604
605 for (int i = 0; i < tabsCount; i++) {
606 sb.append("\t");
607 }
608
609 sb.append("<");
610 sb.append(name);
611 sb.append("><![CDATA[");
612 sb.append(value);
613 sb.append("]]></");
614 sb.append(name);
615 sb.append(">\n");
616 }
617
618 private void _writeProperty(
619 StringBundler sb, Properties properties, String key,
620 String defaultValue) {
621
622 String value = GetterUtil.getString(
623 properties.getProperty(key), defaultValue);
624
625 if (sb.index() > 0) {
626 sb.append(StringPool.NEW_LINE);
627 }
628
629 sb.append(key);
630 sb.append(StringPool.EQUAL);
631 sb.append(value);
632 }
633
634 private static final String[] _TICKET_ID_PREFIXES =
635 {"CLDSVCS", "LPS", "SOS", "SYNC"};
636
637 private static final FileImpl _fileUtil = FileImpl.getInstance();
638
639 private final Set<String> _distinctAuthors = new TreeSet<>();
640 private final Set<String> _distinctLicenses = new TreeSet<>();
641 private final String _latestHASH;
642 private final File _pluginsDir;
643
644 }