From ae08745167aec8c725fcb7037aecca6457c9b51f Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 21 Aug 2024 20:09:59 +0200 Subject: [PATCH 1/5] Bump japicmp to version 0.23.0 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index f5721ef..8cb8cf6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ buildScanRecipes { } dependencies { - api("com.github.siom79.japicmp:japicmp:0.22.0") { + api("com.github.siom79.japicmp:japicmp:0.23.0") { exclude(group = "com.google.guava") exclude(group = "io.airlift") exclude(group = "javax.xml.bind") From 370d02e6f8bb9a9983dfa0fd0b1cbd6170caa4d0 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 21 Aug 2024 20:10:03 +0200 Subject: [PATCH 2/5] Implement new feature: mdOutputFile --- .../gradle/japicmp/JApiCmpWorkerAction.java | 17 +++++++++++++++++ .../japicmp/JapiCmpWorkerConfiguration.java | 3 +++ .../me/champeau/gradle/japicmp/JapicmpTask.java | 5 +++++ 3 files changed, 25 insertions(+) diff --git a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java index 030ed98..74097a2 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java +++ b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java @@ -35,6 +35,7 @@ import japicmp.output.html.HtmlOutput; import japicmp.output.html.HtmlOutputGenerator; import japicmp.output.html.HtmlOutputGeneratorOptions; +import japicmp.output.markdown.MarkdownOutputGenerator; import japicmp.output.semver.SemverOut; import japicmp.output.stdout.StdoutOutputGenerator; import japicmp.output.xml.XmlOutput; @@ -104,6 +105,7 @@ public JApiCmpWorkerAction(final JapiCmpWorkerConfiguration configuration) { configuration.accessModifier, configuration.xmlOutputFile, configuration.htmlOutputFile, + configuration.mdOutputFile, configuration.txtOutputFile, configuration.semverOutputFile, configuration.failOnModification, @@ -261,6 +263,21 @@ private void generateOutput(JarArchiveComparator jarArchiveComparator) { reportFile = htmlOutputFile; } + if (mdOutputFile != null) { + MarkdownOutputGenerator markdownOutputGenerator = new MarkdownOutputGenerator(options, jApiClasses); + String output = markdownOutputGenerator.generate(); + try (BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(mdOutputFile), StandardCharsets.UTF_8) + )) { + writer.write(output); + } catch (IOException ex) { + throw new GradleException("Unable to write markdown report", ex); + } + if (reportFile == null) { + reportFile = mdOutputFile; + } + } + if (txtOutputFile != null) { StdoutOutputGenerator stdoutOutputGenerator = new StdoutOutputGenerator(options, jApiClasses); String output = stdoutOutputGenerator.generate(); diff --git a/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java b/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java index 6e332e8..0467df0 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java +++ b/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java @@ -48,6 +48,7 @@ public class JapiCmpWorkerConfiguration implements Serializable { protected final String accessModifier; protected final File xmlOutputFile; protected final File htmlOutputFile; + protected final File mdOutputFile; protected final File txtOutputFile; protected final File semverOutputFile; protected final boolean failOnModification; @@ -78,6 +79,7 @@ public JapiCmpWorkerConfiguration(final boolean includeSynthetic, final String accessModifier, final File xmlOutputFile, final File htmlOutputFile, + final File mdOutputFile, final File txtOutputFile, final File semverOutputFile, final boolean failOnModification, @@ -107,6 +109,7 @@ public JapiCmpWorkerConfiguration(final boolean includeSynthetic, this.accessModifier = accessModifier; this.xmlOutputFile = xmlOutputFile; this.htmlOutputFile = htmlOutputFile; + this.mdOutputFile = mdOutputFile; this.txtOutputFile = txtOutputFile; this.semverOutputFile = semverOutputFile; this.failOnModification = failOnModification | failOnSourceIncompatibility; diff --git a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java index 20ad727..d797df0 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java +++ b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java @@ -132,6 +132,7 @@ private JapiCmpWorkerConfiguration calculateWorkerConfiguration(List excludeFilterClass) { @Optional public abstract RegularFileProperty getHtmlOutputFile(); + @OutputFile + @Optional + public abstract RegularFileProperty getMdOutputFile(); + @OutputFile @Optional public abstract RegularFileProperty getTxtOutputFile(); From 6b07ebab4ec059699c970e89e81ccd1a4c9feba6 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 21 Aug 2024 20:10:08 +0200 Subject: [PATCH 3/5] Implement new feature: reportOnlySummary --- .../java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java | 2 ++ .../champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java | 3 +++ src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java index 74097a2..33b0ecf 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java +++ b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java @@ -101,6 +101,7 @@ public JApiCmpWorkerAction(final JapiCmpWorkerConfiguration configuration) { configuration.newArchives, configuration.onlyModified, configuration.onlyBinaryIncompatibleModified, + configuration.reportOnlySummary, configuration.failOnSourceIncompatibility, configuration.accessModifier, configuration.xmlOutputFile, @@ -229,6 +230,7 @@ private void generateOutput(JarArchiveComparator jarArchiveComparator) { options.setReportOnlyFilename(true); options.setOutputOnlyModifications(onlyModified); options.setOutputOnlyBinaryIncompatibleModifications(onlyBinaryIncompatibleModified); + options.setReportOnlySummary(reportOnlySummary); options.setIncludeSynthetic(includeSynthetic); options.setAccessModifier(AccessModifier.valueOf(accessModifier.toUpperCase())); File reportFile = null; diff --git a/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java b/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java index 0467df0..b22fb6a 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java +++ b/src/main/java/me/champeau/gradle/japicmp/JapiCmpWorkerConfiguration.java @@ -44,6 +44,7 @@ public class JapiCmpWorkerConfiguration implements Serializable { protected final List newArchives; protected final boolean onlyModified; protected final boolean onlyBinaryIncompatibleModified; + protected final boolean reportOnlySummary; protected final boolean failOnSourceIncompatibility; protected final String accessModifier; protected final File xmlOutputFile; @@ -75,6 +76,7 @@ public JapiCmpWorkerConfiguration(final boolean includeSynthetic, final List newArchives, final boolean onlyModified, final boolean onlyBinaryIncompatibleModified, + final boolean reportOnlySummary, final boolean failOnSourceIncompatibility, final String accessModifier, final File xmlOutputFile, @@ -105,6 +107,7 @@ public JapiCmpWorkerConfiguration(final boolean includeSynthetic, this.newArchives = newArchives; this.onlyModified = onlyModified; this.onlyBinaryIncompatibleModified = onlyBinaryIncompatibleModified; + this.reportOnlySummary = reportOnlySummary; this.failOnSourceIncompatibility = failOnSourceIncompatibility; this.accessModifier = accessModifier; this.xmlOutputFile = xmlOutputFile; diff --git a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java index d797df0..66a0ef7 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java +++ b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java @@ -49,6 +49,7 @@ public JapicmpTask() { getFailOnSourceIncompatibility().convention(false); getIgnoreMissingClasses().convention(false); getIncludeSynthetic().convention(false); + getReportOnlySummary().convention(false); getOnlyBinaryIncompatibleModified().convention(false); getOnlyModified().convention(false); getAccessModifier().convention("public"); @@ -128,6 +129,7 @@ private JapiCmpWorkerConfiguration calculateWorkerConfiguration(List excludeFilterClass) { @Input public abstract Property getOnlyBinaryIncompatibleModified(); + @Input + public abstract Property getReportOnlySummary(); + @Input public abstract Property getFailOnSourceIncompatibility(); From 037e4c3ded7279372f896f9f69400c61a6634152 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 21 Aug 2024 20:10:13 +0200 Subject: [PATCH 4/5] Add functional tests --- .../champeau/gradle/BaseFunctionalTest.groovy | 8 ++++ .../champeau/gradle/BomFunctionalTest.groovy | 1 + .../ClasspathIsUsedFunctionalTest.groovy | 1 + .../gradle/Compare2JarsFunctionalTest.groovy | 2 + .../Compare2LibrariesFunctionalTest.groovy | 1 + .../gradle/ReportsFunctionalTest.groovy | 43 +++++++++++++++++++ .../test-projects/html-report/build.gradle | 13 ++++++ 7 files changed, 69 insertions(+) diff --git a/src/test/groovy/me/champeau/gradle/BaseFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/BaseFunctionalTest.groovy index dbb3fda..40ede4f 100644 --- a/src/test/groovy/me/champeau/gradle/BaseFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/BaseFunctionalTest.groovy @@ -52,6 +52,10 @@ abstract class BaseFunctionalTest extends Specification { } } + void hasmarkdownReport(String lookup = '') { + hasReport('japi', 'md', lookup) + } + void hasTextReport(String lookup = '') { hasReport('japi', 'txt', lookup) } @@ -68,6 +72,10 @@ abstract class BaseFunctionalTest extends Specification { hasReport('rich', 'html', lookup) } + void noMarkdownReport() { + assert !getReport('japi', 'md').exists() + } + void noTxtReport() { assert !getReport('japi', 'txt').exists() } diff --git a/src/test/groovy/me/champeau/gradle/BomFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/BomFunctionalTest.groovy index a8b6d6e..351a52c 100644 --- a/src/test/groovy/me/champeau/gradle/BomFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/BomFunctionalTest.groovy @@ -14,6 +14,7 @@ class BomFunctionalTest extends BaseFunctionalTest { hasTextReport('CLASS FILE FORMAT VERSION: 50.0 <- 50.0') noSemverReport() noHtmlReport() + noMarkdownReport() noRichReport() when: diff --git a/src/test/groovy/me/champeau/gradle/ClasspathIsUsedFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/ClasspathIsUsedFunctionalTest.groovy index 15b67f5..cf6d867 100644 --- a/src/test/groovy/me/champeau/gradle/ClasspathIsUsedFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/ClasspathIsUsedFunctionalTest.groovy @@ -41,6 +41,7 @@ class ClasspathIsUsedFunctionalTest extends BaseFunctionalTest { \t=== UNCHANGED CONSTRUCTOR: PUBLIC Subtype() """) noSemverReport() + noMarkdownReport() noHtmlReport() noRichReport() } diff --git a/src/test/groovy/me/champeau/gradle/Compare2JarsFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/Compare2JarsFunctionalTest.groovy index 3a3c665..bccf352 100644 --- a/src/test/groovy/me/champeau/gradle/Compare2JarsFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/Compare2JarsFunctionalTest.groovy @@ -14,6 +14,7 @@ class Compare2JarsFunctionalTest extends BaseFunctionalTest { hasTextReport('Comparing source compatibility of commons-lang3-3.6.jar against commons-lang3-3.5.jar') hasTextReport('UNCHANGED CLASS: PUBLIC org.apache.commons.lang3.AnnotationUtils') noSemverReport() + noMarkdownReport() noHtmlReport() noRichReport() @@ -33,6 +34,7 @@ class Compare2JarsFunctionalTest extends BaseFunctionalTest { hasTextReport('Comparing source compatibility of commons-lang3-3.6.jar against commons-lang3-3.5.jar') hasTextReport('UNCHANGED CLASS: PUBLIC org.apache.commons.lang3.AnnotationUtils') noSemverReport() + noMarkdownReport() noHtmlReport() noRichReport() diff --git a/src/test/groovy/me/champeau/gradle/Compare2LibrariesFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/Compare2LibrariesFunctionalTest.groovy index 54e381a..b281229 100644 --- a/src/test/groovy/me/champeau/gradle/Compare2LibrariesFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/Compare2LibrariesFunctionalTest.groovy @@ -14,6 +14,7 @@ class Compare2LibrariesFunctionalTest extends BaseFunctionalTest { hasTextReport('Comparing source compatibility of commons-lang3-3.6.jar against commons-lang3-3.5.jar') hasTextReport('UNCHANGED CLASS: PUBLIC org.apache.commons.lang3.AnnotationUtils') noSemverReport() + noMarkdownReport() noHtmlReport() noRichReport() diff --git a/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy index 2f03a11..58e8ee5 100644 --- a/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy @@ -23,6 +23,7 @@ class ReportsFunctionalTest extends BaseFunctionalTest { \t\t\t""") hasHtmlReport('') noTxtReport() + noMarkdownReport() noSemverReport() noRichReport() @@ -42,6 +43,7 @@ class ReportsFunctionalTest extends BaseFunctionalTest { hasRichReport('Binary compatibility report') hasRichReport('A test of rich report') noTxtReport() + noMarkdownReport() noSemverReport() noHtmlReport() @@ -60,6 +62,7 @@ class ReportsFunctionalTest extends BaseFunctionalTest { result.task(":japicmpSemver").outcome == TaskOutcome.SUCCESS hasSemverReport('0.1.0') noTxtReport() + noMarkdownReport() noHtmlReport() noRichReport() @@ -69,4 +72,44 @@ class ReportsFunctionalTest extends BaseFunctionalTest { then: result.task(":japicmpSemver").outcome == TaskOutcome.UP_TO_DATE } + + def "can generate a markdown report"() { + when: + def result = run 'japicmpMarkdown' + + then: + result.task(":japicmpMarkdown").outcome == TaskOutcome.SUCCESS + hasmarkdownReport('# Compatibility Report') + hasmarkdownReport('- **Report only summary**: No') + noTxtReport() + noSemverReport() + noHtmlReport() + noRichReport() + + when: + result = run 'japicmpMarkdown' + + then: + result.task(":japicmpMarkdown").outcome == TaskOutcome.UP_TO_DATE + } + + def "can generate a summary-only markdown report"() { + when: + def result = run 'japicmpMarkdownReportOnlySummary' + + then: + result.task(":japicmpMarkdownReportOnlySummary").outcome == TaskOutcome.SUCCESS + hasmarkdownReport('# Compatibility Report') + hasmarkdownReport('- **Report only summary**: Yes') + noTxtReport() + noSemverReport() + noHtmlReport() + noRichReport() + + when: + result = run 'japicmpMarkdownReportOnlySummary' + + then: + result.task(":japicmpMarkdownReportOnlySummary").outcome == TaskOutcome.UP_TO_DATE + } } diff --git a/src/test/test-projects/html-report/build.gradle b/src/test/test-projects/html-report/build.gradle index e8587fe..3a46cae 100644 --- a/src/test/test-projects/html-report/build.gradle +++ b/src/test/test-projects/html-report/build.gradle @@ -31,6 +31,19 @@ task japicmpRich(type: me.champeau.gradle.japicmp.JapicmpTask) { } } +task japicmpMarkdown(type: me.champeau.gradle.japicmp.JapicmpTask) { + oldClasspath.from(configurations.baseline) + newClasspath.from(configurations.current) + mdOutputFile = layout.buildDirectory.file('reports/japi.md').get().asFile +} + +task japicmpMarkdownReportOnlySummary(type: me.champeau.gradle.japicmp.JapicmpTask) { + oldClasspath.from(configurations.baseline) + newClasspath.from(configurations.current) + mdOutputFile = layout.buildDirectory.file('reports/japi.md').get().asFile + reportOnlySummary = true +} + task japicmpSemver(type: me.champeau.gradle.japicmp.JapicmpTask) { oldClasspath.from(configurations.baseline) newClasspath.from(configurations.current) From a7d1dd3187041b0f4ec6d2d7c956888dac5d35dd Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 21 Aug 2024 20:10:17 +0200 Subject: [PATCH 5/5] Document new features --- README.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.adoc b/README.adoc index c645915..43aa604 100644 --- a/README.adoc +++ b/README.adoc @@ -52,6 +52,7 @@ oldArchives:: The jar files which will be used as the baseline for comparison. T newArchives:: The jar files we want to analyze. Type: Type: _FileCollection_. onlyModified:: Outputs only modified classes/methods. If not set to true, all classes and methods are printed. Type: _boolean_. Default value: _false_ onlyBinaryIncompatibleModified:: Outputs only classes/methods with modifications that result in binary incompatibility. Type: _boolean_. Default value: _false_ +reportOnlySummary:: Reports only a breakdown of classes and their status. Type: _boolean_. Default value: _false_ packageIncludes:: List of package names to include, * can be used as wildcard. Type: _List_ packageExcludes:: List of package names to exclude, * can be used as wildcard. Type: _List_ classIncludes:: List of classes to include. Type: _List_ @@ -68,6 +69,7 @@ failOnSourceIncompatibility:: Fails if the changes result in source level incomp failOnModification:: When set to true, the build fails in case a modification has been detected. Type: _boolean_. Default value: _false_ xmlOutputFile:: Path to the generated XML report. Type: _File_. Default value: _null_ htmlOutputFile:: Path to the generated HTML report. Type: _File_. Default value: _null_ +mdOutputFile:: Path to the generated Markdown report. Type: _File_. Default value: _null_ txtOutputFile:: Path to the generated TXT report. Type: _File_. Default value: _null_ semverOutputFile:: Path to the generated semantic versioning report. Type: _File_. Default value: _null_ includeSynthetic:: Synthetic classes and class members (like e.g. bridge methods) are not tracked per default. This new option enables the tracking of such kind of classes and class members