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