diff --git a/build.gradle b/build.gradle index f5d6ce59..af4e124d 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { classpath 'com.palantir.jakartapackagealignment:jakarta-package-alignment:0.6.0' classpath 'com.palantir.gradle.externalpublish:gradle-external-publish-plugin:1.11.0' classpath 'com.palantir.javaformat:gradle-palantir-java-format:2.47.0' + classpath 'com.palantir.gradle.plugintesting:gradle-plugin-testing:0.10.0' } } @@ -33,6 +34,7 @@ apply plugin: 'java-gradle-plugin' apply plugin: 'java-library' apply plugin: 'groovy' apply plugin: 'com.palantir.external-publish-jar' +apply plugin: 'com.palantir.gradle-plugin-testing' sourceCompatibility = 1.8 @@ -48,10 +50,12 @@ dependencies { testImplementation platform('org.junit:junit-bom') testImplementation 'com.netflix.nebula:nebula-test' - testImplementation "org.junit.jupiter:junit-jupiter" testImplementation 'org.mockito:mockito-core' testImplementation 'org.assertj:assertj-core' - testRuntimeOnly "org.junit.vintage:junit-vintage-engine" + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.platform:junit-platform-engine' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testRuntimeOnly 'org.junit.vintage:junit-vintage-engine' annotationProcessor "org.immutables:value" annotationProcessor "org.immutables:serial" @@ -68,6 +72,7 @@ test { environment = System.getenv().entrySet().stream() .filter { entry -> entry.key != 'CIRCLE_TEST_REPORTS' } .collect(Collectors.toMap({ it.key }, { it.value })) + systemProperty 'ignoreDeprecations', 'true' } gradlePlugin { diff --git a/src/main/java/org/revapi/gradle/ConjureProjectFilters.java b/src/main/java/org/revapi/gradle/ConjureProjectFilters.java index e8c9741e..ce6ce938 100644 --- a/src/main/java/org/revapi/gradle/ConjureProjectFilters.java +++ b/src/main/java/org/revapi/gradle/ConjureProjectFilters.java @@ -18,7 +18,6 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import java.util.Optional; -import org.gradle.api.Project; final class ConjureProjectFilters { private static final ArrayNode CHECKS_FOR_CLIENT_PROJECTS = RevapiConfig.createArrayNode() @@ -29,16 +28,12 @@ final class ConjureProjectFilters { private ConjureProjectFilters() {} - public static RevapiConfig forProject(Project project) { - boolean isConjure = Optional.ofNullable(project.getParent()) - .map(parentProject -> parentProject.getPluginManager().hasPlugin("com.palantir.conjure")) - .orElse(false); - + public static RevapiConfig from(String projectName, boolean isConjure) { if (!isConjure) { return RevapiConfig.empty(); } - return checksForProjectName(project.getName()) + return checksForProjectName(projectName) .map(checks -> RevapiConfig.empty().withExtension(CheckWhitelist.EXTENSION_ID, checks)) .orElseGet(RevapiConfig::empty); } diff --git a/src/main/java/org/revapi/gradle/GitVersionUtils.java b/src/main/java/org/revapi/gradle/GitVersionUtils.java index f93c3ad6..d3c5640a 100644 --- a/src/main/java/org/revapi/gradle/GitVersionUtils.java +++ b/src/main/java/org/revapi/gradle/GitVersionUtils.java @@ -16,39 +16,54 @@ package org.revapi.gradle; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Spliterator; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import org.gradle.api.Project; +import javax.inject.Inject; +import org.gradle.api.file.Directory; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.process.ExecOutput; import org.gradle.process.ExecResult; import org.immutables.value.Value; -final class GitVersionUtils { - private GitVersionUtils() {} +public abstract class GitVersionUtils { - public static Stream previousGitTags(Project project) { - return StreamSupport.stream(new PreviousGitTags(project), false) - .filter(tag -> !isInitial000Tag(project, tag)) + @Inject + protected abstract ProviderFactory getProviderFactory(); + + @Inject + protected abstract ProjectLayout getProjectLayout(); + + public final Stream previousGitTags() { + return StreamSupport.stream( + new PreviousGitTags( + getProviderFactory(), getProjectLayout().getProjectDirectory()), + false) + .filter(tag -> !isInitial000Tag( + getProviderFactory(), getProjectLayout().getProjectDirectory(), tag)) .map(GitVersionUtils::stripVFromTag); } - private static Optional previousGitTagFromRef(Project project, String ref) { + private static Optional previousGitTagFromRef( + ProviderFactory providerFactory, Directory directory, String ref) { String beforeLastRef = ref + "^"; - GitResult beforeLastRefTypeResult = execute(project, "git", "cat-file", "-t", beforeLastRef); + GitResult beforeLastRefTypeResult = execute(providerFactory, directory, "git", "cat-file", "-t", beforeLastRef) + .get(); boolean thereIsNoCommitBeforeTheRef = !beforeLastRefTypeResult.stdout().equals("commit"); if (thereIsNoCommitBeforeTheRef) { return Optional.empty(); } - GitResult describeResult = execute(project, "git", "describe", "--tags", "--abbrev=0", beforeLastRef); + GitResult describeResult = execute( + providerFactory, directory, "git", "describe", "--tags", "--abbrev=0", beforeLastRef) + .get(); if (describeResult.stderr().contains("No tags can describe") || describeResult.stderr().contains("No names found, cannot describe anything")) { @@ -58,14 +73,15 @@ private static Optional previousGitTagFromRef(Project project, String re return Optional.of(describeResult.stdoutOrThrowIfNonZero()); } - private static boolean isInitial000Tag(Project project, String tag) { + private static boolean isInitial000Tag(ProviderFactory providerFactory, Directory directory, String tag) { if (!tag.equals("0.0.0")) { return false; } - GitResult foo = execute(project, "git", "rev-parse", "--verify", "--quiet", "0.0.0^"); - boolean parentDoesNotExist = foo.exitCode() != 0; - return parentDoesNotExist; + return execute(providerFactory, directory, "git", "rev-parse", "--verify", "--quiet", "0.0.0^") + .get() + .exitCode() + != 0; } private static String stripVFromTag(String tag) { @@ -76,22 +92,24 @@ private static String stripVFromTag(String tag) { } } - private static GitResult execute(Project project, String... command) { - ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - - ExecResult execResult = project.exec(spec -> { - spec.setCommandLine(Arrays.asList(command)); - spec.setStandardOutput(stdout); - spec.setErrorOutput(stderr); - spec.setIgnoreExitValue(true); + private static Provider execute( + ProviderFactory providerFactory, Directory directory, String... command) { + ExecOutput output = providerFactory.exec(execSpec -> { + execSpec.commandLine((Object[]) command); + execSpec.setIgnoreExitValue(true); + execSpec.setWorkingDir(directory); }); - return GitResult.builder() - .exitCode(execResult.getExitValue()) - .stdout(new String(stdout.toByteArray(), StandardCharsets.UTF_8).trim()) - .stderr(new String(stderr.toByteArray(), StandardCharsets.UTF_8).trim()) - .build(); + Provider stdout = output.getStandardOutput().getAsText(); + Provider stderr = output.getStandardError().getAsText(); + Provider result = output.getResult(); + + return stdout.zip(stderr, (out, err) -> new Object[] {out, err}) + .zip(result, (outErr, res) -> GitResult.builder() + .exitCode(res.getExitValue()) + .stdout(((String) outErr[0]).trim()) + .stderr(((String) outErr[1]).trim()) + .build()); } @Value.Immutable @@ -124,16 +142,18 @@ static Builder builder() { } private static final class PreviousGitTags implements Spliterator { - private final Project project; + private final ProviderFactory providerFactory; + private final Directory directory; private String lastSeenRef = "HEAD"; - PreviousGitTags(Project project) { - this.project = project; + PreviousGitTags(ProviderFactory providerFactory, Directory directory) { + this.providerFactory = providerFactory; + this.directory = directory; } @Override public boolean tryAdvance(Consumer action) { - Optional tag = previousGitTagFromRef(project, lastSeenRef); + Optional tag = previousGitTagFromRef(providerFactory, directory, lastSeenRef); if (!tag.isPresent()) { return false; diff --git a/src/main/java/org/revapi/gradle/RevapiAcceptBreakTask.java b/src/main/java/org/revapi/gradle/RevapiAcceptBreakTask.java index 46818cb7..0d0b4b87 100644 --- a/src/main/java/org/revapi/gradle/RevapiAcceptBreakTask.java +++ b/src/main/java/org/revapi/gradle/RevapiAcceptBreakTask.java @@ -20,6 +20,7 @@ import java.util.Optional; import org.gradle.api.DefaultTask; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; @@ -27,63 +28,49 @@ import org.revapi.gradle.config.GroupNameVersion; import org.revapi.gradle.config.Justification; -public class RevapiAcceptBreakTask extends DefaultTask { +public abstract class RevapiAcceptBreakTask extends DefaultTask { private static final String CODE_OPTION = "code"; private static final String OLD_OPTION = "old"; private static final String NEW_OPTION = "new"; private static final String JUSTIFICATION_OPTION = "justification"; - private final Property configManager = - getProject().getObjects().property(ConfigManager.class); - private final Property code = getProject().getObjects().property(String.class); - private final Property oldElement = getProject().getObjects().property(String.class); - private final Property newElement = getProject().getObjects().property(String.class); - private final Property justification = - getProject().getObjects().property(Justification.class); - - public RevapiAcceptBreakTask() { - getOutputs().upToDateWhen(_ignored -> false); - } - - @Internal - final Property getConfigManager() { - return configManager; - } - + @Input @Option(option = CODE_OPTION, description = "Revapi change code") - public final void setCode(String codeString) { - this.code.set(codeString); - } + public abstract Property getCode(); + @Input @Option(option = OLD_OPTION, description = "Old API element") - public final void setOldElement(String oldElementString) { - this.oldElement.set(oldElementString); - } + public abstract Property getOldElement(); + @Input @Option(option = NEW_OPTION, description = "New API element") - public final void setNewElement(String newElementString) { - this.newElement.set(newElementString); - } + public abstract Property getNewElement(); + @Input @Option(option = JUSTIFICATION_OPTION, description = "Justification for why these breaks are ok") - public final void setJustification(String justificationString) { - this.justification.set(Justification.fromString(justificationString)); - } + public abstract Property getJustificationString(); + + @Input + protected abstract Property getGroupNameVersion(); + + @Internal + protected abstract Property getConfigManager(); @TaskAction public final void addVersionOverride() { - ensurePresent(code, CODE_OPTION); - ensurePresent(justification, JUSTIFICATION_OPTION); + ensurePresent(getCode(), CODE_OPTION); + ensurePresent(getJustificationString(), JUSTIFICATION_OPTION); - configManager + getConfigManager() .get() .modifyConfigFile(revapiConfig -> revapiConfig.addAcceptedBreaks( - oldGroupNameVersion(), + getGroupNameVersion().get(), Collections.singleton(AcceptedBreak.builder() - .code(code.get()) - .oldElement(Optional.ofNullable(oldElement.getOrNull())) - .newElement(Optional.ofNullable(newElement.getOrNull())) - .justification(justification.get()) + .code(getCode().get()) + .oldElement(Optional.ofNullable(getOldElement().getOrNull())) + .newElement(Optional.ofNullable(getNewElement().getOrNull())) + .justification(Justification.fromString( + getJustificationString().get())) .build()))); } @@ -92,8 +79,4 @@ private void ensurePresent(Property prop, String option) { throw new IllegalArgumentException("Please supply the --" + option + " param to this task"); } } - - private GroupNameVersion oldGroupNameVersion() { - return getProject().getExtensions().getByType(RevapiExtension.class).oldGroupNameVersion(); - } } diff --git a/src/main/java/org/revapi/gradle/RevapiAnalyzeTask.java b/src/main/java/org/revapi/gradle/RevapiAnalyzeTask.java index f9fd0da7..49d8ad31 100644 --- a/src/main/java/org/revapi/gradle/RevapiAnalyzeTask.java +++ b/src/main/java/org/revapi/gradle/RevapiAnalyzeTask.java @@ -42,63 +42,40 @@ import org.slf4j.LoggerFactory; @CacheableTask -public class RevapiAnalyzeTask extends DefaultTask { +public abstract class RevapiAnalyzeTask extends DefaultTask { private static final Logger log = LoggerFactory.getLogger(RevapiAnalyzeTask.class); - private final SetProperty acceptedBreaks = - getProject().getObjects().setProperty(AcceptedBreak.class); - private final Property newApiJars = - getProject().getObjects().property(FileCollection.class); - private final Property newApiDependencyJars = - getProject().getObjects().property(FileCollection.class); - private final Property jarsToReportBreaks = - getProject().getObjects().property(FileCollection.class); - private final Property oldApiJars = - getProject().getObjects().property(FileCollection.class); - private final Property oldApiDependencyJars = - getProject().getObjects().property(FileCollection.class); - private final RegularFileProperty analysisResultsFile = - getProject().getObjects().fileProperty(); + @Input + protected abstract SetProperty getAcceptedBreaks(); @Input - public final SetProperty getAcceptedBreaks() { - return acceptedBreaks; - } + protected abstract Property getProjectName(); + + @Input + protected abstract Property getIsConjure(); @CompileClasspath - public final Property getNewApiJars() { - return newApiJars; - } + protected abstract Property getNewApiJars(); @CompileClasspath - public final Property getNewApiDependencyJars() { - return newApiDependencyJars; - } + protected abstract Property getNewApiDependencyJars(); @CompileClasspath - public final Property getJarsToReportBreaks() { - return jarsToReportBreaks; - } + protected abstract Property getJarsToReportBreaks(); @CompileClasspath - public final Property getOldApiJars() { - return oldApiJars; - } + protected abstract Property getOldApiJars(); @CompileClasspath - public final Property getOldApiDependencyJars() { - return oldApiDependencyJars; - } + protected abstract Property getOldApiDependencyJars(); @OutputFile - public final RegularFileProperty getAnalysisResultsFile() { - return analysisResultsFile; - } + protected abstract RegularFileProperty getAnalysisResultsFile(); @TaskAction protected final void runRevapi() throws Exception { - API oldApi = api(oldApiJars, oldApiDependencyJars); - API newApi = api(newApiJars, newApiDependencyJars); + API oldApi = api(getOldApiJars(), getOldApiDependencyJars()); + API newApi = api(getNewApiJars(), getNewApiDependencyJars()); log.info("Old API: {}", oldApi); log.info("New API: {}", newApi); @@ -111,13 +88,14 @@ protected final void runRevapi() throws Exception { .build(); RevapiConfig revapiConfig = RevapiConfig.mergeAll( - RevapiConfig.defaults(jarsToReportBreaks.get()), + RevapiConfig.defaults(getJarsToReportBreaks().get()), RevapiConfig.empty() .withTextReporter( "gradle-revapi-results.ftl", - analysisResultsFile.getAsFile().get()), + getAnalysisResultsFile().getAsFile().get()), revapiIgnores(), - ConjureProjectFilters.forProject(getProject()), + ConjureProjectFilters.from( + getProjectName().get(), getIsConjure().get()), ImmutablesFilter.CONFIG); log.info("revapi config:\n{}", revapiConfig.configAsString()); @@ -133,7 +111,7 @@ protected final void runRevapi() throws Exception { } private RevapiConfig revapiIgnores() { - return RevapiConfig.empty().withIgnoredBreaks(acceptedBreaks.get()); + return RevapiConfig.empty().withIgnoredBreaks(getAcceptedBreaks().get()); } private API api(Provider apiJars, Provider dependencyJars) { diff --git a/src/main/java/org/revapi/gradle/RevapiExtension.java b/src/main/java/org/revapi/gradle/RevapiExtension.java index bd5e4405..bd7b5340 100644 --- a/src/main/java/org/revapi/gradle/RevapiExtension.java +++ b/src/main/java/org/revapi/gradle/RevapiExtension.java @@ -22,17 +22,21 @@ import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Nested; import org.revapi.gradle.config.GroupAndName; import org.revapi.gradle.config.GroupNameVersion; import org.revapi.gradle.config.Version; @SuppressWarnings("DesignForExtension") -public class RevapiExtension { +public abstract class RevapiExtension { private final Property oldGroup; private final Property oldName; private final ListProperty oldVersions; private final Provider oldGroupAndName; + @Nested + protected abstract GitVersionUtils getGitVersionUtils(); + public RevapiExtension(Project project) { this.oldGroup = project.getObjects().property(String.class); this.oldGroup.set( @@ -43,8 +47,7 @@ public RevapiExtension(Project project) { this.oldVersions = project.getObjects().listProperty(String.class); this.oldVersions.set(project.getProviders() - .provider( - () -> GitVersionUtils.previousGitTags(project).limit(3).collect(Collectors.toList()))); + .provider(() -> getGitVersionUtils().previousGitTags().limit(3).collect(Collectors.toList()))); this.oldGroupAndName = project.provider(() -> GroupAndName.builder().group(oldGroup.get()).name(oldName.get()).build()); diff --git a/src/main/java/org/revapi/gradle/RevapiPlugin.java b/src/main/java/org/revapi/gradle/RevapiPlugin.java index 8e6feb3b..7081f15c 100644 --- a/src/main/java/org/revapi/gradle/RevapiPlugin.java +++ b/src/main/java/org/revapi/gradle/RevapiPlugin.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.gradle.api.DefaultTask; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; @@ -108,6 +109,30 @@ public void apply(Project project) { task.getAnalysisResultsFile().set(new File(project.getBuildDir(), "revapi/revapi-results.json")); + task.getProjectName().set(project.getName()); + task.getIsConjure() + .set(Optional.ofNullable(project.getParent()) + .map(parentProject -> + parentProject.getPluginManager().hasPlugin("com.palantir.conjure")) + .orElse(false)); + + task.onlyIf(oldApiIsPresent); + }); + + TaskProvider acceptBreakTaskTaskProvider = project.getTasks() + .register(ACCEPT_BREAK_TASK_NAME, RevapiAcceptBreakTask.class, task -> { + task.getConfigManager().set(configManager); + task.getGroupNameVersion().set(project.getProviders().provider(extension::oldGroupNameVersion)); + task.doNotTrackState("This task should always run."); + }); + + TaskProvider acceptAllBreaksProjectTaskProvider = project.getTasks() + .register(ACCEPT_ALL_BREAKS_TASK_NAME, RevapiAcceptAllBreaksTask.class, task -> { + task.dependsOn(analyzeTask); + + task.getOldGroupNameVersion().set(project.getProviders().provider(extension::oldGroupNameVersion)); + task.getConfigManager().set(configManager); + task.getAnalysisResultsFile().set(analyzeTask.flatMap(RevapiAnalyzeTask::getAnalysisResultsFile)); task.onlyIf(oldApiIsPresent); }); @@ -116,27 +141,19 @@ public void apply(Project project) { task.dependsOn(analyzeTask); task.getAnalysisResultsFile().set(analyzeTask.flatMap(RevapiAnalyzeTask::getAnalysisResultsFile)); task.getJunitOutputFile().set(junitOutput(project)); + task.getAcceptAllBreaksTaskPath().set(acceptBreakTaskTaskProvider.map(DefaultTask::getPath)); + task.getAcceptAllBreaksProjectTaskPath() + .set(acceptAllBreaksProjectTaskProvider.map(DefaultTask::getPath)); task.onlyIf(oldApiIsPresent); }); project.getTasks().findByName(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(reportTask); - project.getTasks().register(ACCEPT_ALL_BREAKS_TASK_NAME, RevapiAcceptAllBreaksTask.class, task -> { - task.dependsOn(analyzeTask); - - task.getOldGroupNameVersion().set(project.getProviders().provider(extension::oldGroupNameVersion)); - task.getConfigManager().set(configManager); - task.getAnalysisResultsFile().set(analyzeTask.flatMap(RevapiAnalyzeTask::getAnalysisResultsFile)); - task.onlyIf(oldApiIsPresent); - }); - project.getTasks().register(VERSION_OVERRIDE_TASK_NAME, RevapiVersionOverrideTask.class, task -> { task.getConfigManager().set(configManager); - }); - - project.getTasks().register(ACCEPT_BREAK_TASK_NAME, RevapiAcceptBreakTask.class, task -> { - task.getConfigManager().set(configManager); + task.getGroupNameVersion().set(project.getProviders().provider(extension::oldGroupNameVersion)); + task.doNotTrackState("This task should always run."); }); } diff --git a/src/main/java/org/revapi/gradle/RevapiReportTask.java b/src/main/java/org/revapi/gradle/RevapiReportTask.java index 315aef98..bae0de62 100644 --- a/src/main/java/org/revapi/gradle/RevapiReportTask.java +++ b/src/main/java/org/revapi/gradle/RevapiReportTask.java @@ -27,56 +27,46 @@ import java.util.Map; import org.gradle.api.DefaultTask; import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; import org.revapi.gradle.config.Justification; -public class RevapiReportTask extends DefaultTask { - private final RegularFileProperty analysisResultsFile = - getProject().getObjects().fileProperty(); - private final RegularFileProperty junitOutputFile = - getProject().getObjects().fileProperty(); +public abstract class RevapiReportTask extends DefaultTask { + + @Internal + protected abstract Property getAcceptAllBreaksTaskPath(); + + @Internal + protected abstract Property getAcceptAllBreaksProjectTaskPath(); @InputFile - public final RegularFileProperty getAnalysisResultsFile() { - return analysisResultsFile; - } + protected abstract RegularFileProperty getAnalysisResultsFile(); @OutputFile - public final RegularFileProperty getJunitOutputFile() { - return junitOutputFile; - } + protected abstract RegularFileProperty getJunitOutputFile(); @TaskAction public final void reportBreaks() throws Exception { AnalysisResults results = - AnalysisResults.fromFile(analysisResultsFile.getAsFile().get()); + AnalysisResults.fromFile(getAnalysisResultsFile().getAsFile().get()); Configuration freeMarkerConfiguration = createFreeMarkerConfiguration(); Map templateData = new HashMap<>(); templateData.put("results", results); - templateData.put( - "acceptBreakTask", - getProject() - .getTasks() - .withType(RevapiAcceptBreakTask.class) - .getByName(RevapiPlugin.ACCEPT_BREAK_TASK_NAME) - .getPath()); + templateData.put("acceptBreakTask", getAcceptAllBreaksTaskPath().get()); templateData.put( "acceptAllBreaksProjectTask", - getProject() - .getTasks() - .withType(RevapiAcceptAllBreaksTask.class) - .getByName(RevapiPlugin.ACCEPT_ALL_BREAKS_TASK_NAME) - .getPath()); + getAcceptAllBreaksProjectTaskPath().get()); templateData.put("acceptAllBreaksEverywhereTask", RevapiPlugin.ACCEPT_ALL_BREAKS_TASK_NAME); templateData.put("explainWhy", Justification.YOU_MUST_ENTER_JUSTIFICATION); Template junitTemplate = freeMarkerConfiguration.getTemplate("gradle-revapi-junit-template.ftl"); junitTemplate.process( templateData, - Files.newBufferedWriter(junitOutputFile.getAsFile().get().toPath(), StandardCharsets.UTF_8)); + Files.newBufferedWriter(getJunitOutputFile().getAsFile().get().toPath(), StandardCharsets.UTF_8)); Template textTemplate = freeMarkerConfiguration.getTemplate("gradle-revapi-text-template.ftl"); StringWriter textOutputWriter = new StringWriter(); diff --git a/src/main/java/org/revapi/gradle/RevapiVersionOverrideTask.java b/src/main/java/org/revapi/gradle/RevapiVersionOverrideTask.java index 8364481b..4be4bd61 100644 --- a/src/main/java/org/revapi/gradle/RevapiVersionOverrideTask.java +++ b/src/main/java/org/revapi/gradle/RevapiVersionOverrideTask.java @@ -18,45 +18,34 @@ import org.gradle.api.DefaultTask; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; import org.revapi.gradle.config.GroupNameVersion; -public class RevapiVersionOverrideTask extends DefaultTask { +public abstract class RevapiVersionOverrideTask extends DefaultTask { public static final String REPLACEMENT_VERSION_OPTION = "replacement-version"; - private final Property configManager = - getProject().getObjects().property(ConfigManager.class); - private final Property replacementVersion = - getProject().getObjects().property(String.class); - - public RevapiVersionOverrideTask() { - getOutputs().upToDateWhen(_ignored -> false); - } + @Input + protected abstract Property getGroupNameVersion(); @Internal - final Property getConfigManager() { - return configManager; - } + protected abstract Property getConfigManager(); + @Input @Option(option = REPLACEMENT_VERSION_OPTION, description = "The version to use instead of the default oldVersion") - public final void setReplacementVersion(String replacementVersionValue) { - replacementVersion.set(replacementVersionValue); - } + public abstract Property getReplacementVersion(); @TaskAction public final void addVersionOverride() { - if (!replacementVersion.isPresent()) { - throw new RuntimeException("Please supply the --" + REPLACEMENT_VERSION_OPTION + " param this task"); + if (!getReplacementVersion().isPresent()) { + throw new RuntimeException("Please supply the --" + REPLACEMENT_VERSION_OPTION + " param to this task"); } - configManager + getConfigManager() .get() - .modifyConfigFile(config -> config.addVersionOverride(oldGroupNameVersion(), replacementVersion.get())); - } - - private GroupNameVersion oldGroupNameVersion() { - return getProject().getExtensions().getByType(RevapiExtension.class).oldGroupNameVersion(); + .modifyConfigFile(config -> config.addVersionOverride( + getGroupNameVersion().get(), getReplacementVersion().get())); } } diff --git a/src/test/groovy/org/revapi/gradle/GitVersionUtilsSpec.groovy b/src/test/groovy/org/revapi/gradle/GitVersionUtilsSpec.groovy index 29b57373..fe4ccdab 100644 --- a/src/test/groovy/org/revapi/gradle/GitVersionUtilsSpec.groovy +++ b/src/test/groovy/org/revapi/gradle/GitVersionUtilsSpec.groovy @@ -113,6 +113,7 @@ class GitVersionUtilsSpec extends AbstractProjectSpec { } private List previousGitTags() { - GitVersionUtils.previousGitTags(getProject()).collect(Collectors.toList()) + GitVersionUtils utils = getProject().objects.newInstance(GitVersionUtils.class) + utils.previousGitTags().collect(Collectors.toList()) } } diff --git a/src/test/groovy/org/revapi/gradle/RevapiSpec.groovy b/src/test/groovy/org/revapi/gradle/RevapiSpec.groovy index fff69f94..ca77808b 100644 --- a/src/test/groovy/org/revapi/gradle/RevapiSpec.groovy +++ b/src/test/groovy/org/revapi/gradle/RevapiSpec.groovy @@ -16,19 +16,19 @@ package org.revapi.gradle +import com.palantir.gradle.plugintesting.ConfigurationCacheSpec import spock.lang.Ignore import java.util.regex.Pattern -import nebula.test.IntegrationSpec -import nebula.test.functional.ExecutionResult +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome import spock.util.environment.RestoreSystemProperties -class RevapiSpec extends IntegrationSpec { +class RevapiSpec extends ConfigurationCacheSpec { private Git git def setup() { git = new Git(projectDir) - System.setProperty("ignoreDeprecations", "true") } def 'fails when comparing produced jar versus some random other jar'() { @@ -118,17 +118,18 @@ class RevapiSpec extends IntegrationSpec { public class Foo extends org.junit.rules.ExternalResource { } '''.stripIndent() - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output and: buildFile.text = buildFile.text.replace('implementation', 'compileOnly') then: - def executionResult = runTasks('revapi') - println executionResult.standardOutput - println executionResult.standardError - executionResult.rethrowFailure() + def buildResult = runTasksSuccessfully('revapi') + println buildResult.output + if (buildResult.task(':revapi').outcome != TaskOutcome.SUCCESS) { + throw new RuntimeException("Task failed: ${buildResult.output}") + } } def 'revapiAcceptAllBreaks succeeds when there are no breaking changes'() { @@ -149,7 +150,7 @@ class RevapiSpec extends IntegrationSpec { """.stripIndent() then: - runTasksSuccessfully("revapiAcceptAllBreaks", "--justification", "fight me") + runTasksWithConfigurationCache("revapiAcceptAllBreaks", "--justification", "fight me") } def 'does not error out when project has a version greater than the "old version"'() { @@ -252,9 +253,9 @@ class RevapiSpec extends IntegrationSpec { """.stripIndent() then: - def executionResult = runTasksSuccessfully('revapi') - executionResult.wasSkipped(':revapiAnalyze') - executionResult.wasSkipped(':revapi') + def buildResult = runTasksSuccessfully('revapi') + assert buildResult.task(':revapiAnalyze').outcome == TaskOutcome.SKIPPED + assert buildResult.task(':revapi').outcome == TaskOutcome.SKIPPED } def 'when the previous git tag has failed to publish, it will look back up to a further git tag'() { @@ -271,7 +272,7 @@ class RevapiSpec extends IntegrationSpec { buildFile << """ plugins { - id 'com.palantir.git-version' version '3.1.0' + id 'com.palantir.git-version' version '4.0.0' } apply plugin: '${TestConstants.PLUGIN_NAME}' @@ -296,7 +297,7 @@ class RevapiSpec extends IntegrationSpec { git.command 'git commit -m 0.1.0' git.command 'git tag 0.1.0' - runTasksSuccessfully('publish') + runTasksWithConfigurationCache('publish') and: git.command 'git commit --allow-empty -m publish-failed' @@ -310,8 +311,8 @@ class RevapiSpec extends IntegrationSpec { git.command 'git commit -am new-work' then: - def standardError = runTasksWithFailure('revapi').standardError - assert standardError.contains('willBeRemoved') + def buildResult = runTasksWithFailure('revapi') + assert buildResult.output.contains('willBeRemoved') } def 'if there are no published versions of the library at all, ./gradlew revapi doesnt fail'() { @@ -320,9 +321,9 @@ class RevapiSpec extends IntegrationSpec { writeHelloWorld() then: - def executionResult = runTasksSuccessfully('revapi') - executionResult.wasSkipped(':revapiAnalyze') - executionResult.wasSkipped(':revapi') + def buildResult = runTasksSuccessfully('revapi') + assert buildResult.task(':revapiAnalyze').outcome == TaskOutcome.SKIPPED + assert buildResult.task(':revapi').outcome == TaskOutcome.SKIPPED } def 'if there are no published versions of the library at all, ./gradlew revapiAcceptAllBreaks is a no-op'() { @@ -331,9 +332,9 @@ class RevapiSpec extends IntegrationSpec { writeHelloWorld() then: - def executionResult = runTasksSuccessfully('revapiAcceptAllBreaks') - executionResult.wasSkipped(':revapiAnalyze') - executionResult.wasSkipped(':revapiAcceptAllBreaks') + def buildResult = runTasksSuccessfully('revapiAcceptAllBreaks') + assert buildResult.task(':revapiAnalyze').outcome == TaskOutcome.SKIPPED + assert buildResult.task(':revapiAcceptAllBreaks').outcome == TaskOutcome.SKIPPED } def 'if there are no published versions of the library at all, ./gradlew revapiAcceptBreak doesnt fail'() { @@ -342,9 +343,9 @@ class RevapiSpec extends IntegrationSpec { writeHelloWorld() then: - def executionResult = runTasksSuccessfully('revapiAcceptBreak', '--justification', 'foo', '--code', 'bar', + def buildResult = runTasksSuccessfully('revapiAcceptBreak', '--justification', 'foo', '--code', 'bar', '--old', 'old', '--new', 'new') - executionResult.wasExecuted(':revapiAcceptBreak') + assert buildResult.task(':revapiAcceptBreak').outcome == TaskOutcome.SUCCESS } private File setupUnpublishedLibrary() { @@ -417,7 +418,7 @@ class RevapiSpec extends IntegrationSpec { and: !revapiYml.exists() - runTasksSuccessfully("revapiAcceptAllBreaks", "--justification", "it's all good :)") + runTasksWithConfigurationCache("revapiAcceptAllBreaks", "--justification", "it's all good :)") revapiYml.text.contains('java.class.removed') then: @@ -506,13 +507,13 @@ class RevapiSpec extends IntegrationSpec { '''.stripIndent() when: - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output writeToFile two, 'src/main/java/foo/Foo.java', originalJavaFile.text originalJavaFile.delete() and: - println runTasksSuccessfully("revapi").standardOutput + println runTasksSuccessfully("revapi").output and: def oneBuildGradle = new File(one, 'build.gradle') @@ -565,12 +566,12 @@ class RevapiSpec extends IntegrationSpec { '''.stripIndent() and: - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output javaFileInDependentProject.text = javaFileInDependentProject.text.replace('}', 'void foo();\n}') then: - println runTasksSuccessfully("revapi").standardOutput + println runTasksSuccessfully("revapi").output } def 'should not say there are breaks in api dependencies when nothing has changed'() { @@ -608,10 +609,10 @@ class RevapiSpec extends IntegrationSpec { ''' and: - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output then: - println runTasksSuccessfully("revapi").standardOutput + println runTasksSuccessfully("revapi").output } def 'ignores scala classes'() { @@ -665,10 +666,10 @@ class RevapiSpec extends IntegrationSpec { class Foo {} '''.stripIndent() - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output then: - println runTasksSuccessfully("revapi").standardOutput + println runTasksSuccessfully("revapi").output } def 'detects breaks in groovy code'() { @@ -705,7 +706,7 @@ class RevapiSpec extends IntegrationSpec { } '''.stripIndent() - println runTasksSuccessfully("publish").standardOutput + println runTasksWithConfigurationCache("publish").output and: writeToFile groovyFile, ''' @@ -714,11 +715,11 @@ class RevapiSpec extends IntegrationSpec { '''.stripIndent() then: - def stderr = runRevapiExpectingFailure() + def output = runRevapiExpectingFailure() - assert stderr.contains('java.method.removed') - assert stderr.contains('method java.lang.String foo.Foo::getSomeProperty()') - assert stderr.contains('method void foo.Foo::setSomeProperty(java.lang.String)') + assert output.contains('java.method.removed') + assert output.contains('method java.lang.String foo.Foo::getSomeProperty()') + assert output.contains('method void foo.Foo::setSomeProperty(java.lang.String)') } def 'does not throw exception when baseline-circleci is applied before this plugin'() { @@ -735,7 +736,13 @@ class RevapiSpec extends IntegrationSpec { } dependencies { - classpath 'com.palantir.baseline:gradle-baseline-java:5.61.0' + classpath 'com.palantir.baseline:gradle-baseline-java:6.48.0' + } + } + + allprojects { + repositories { + mavenCentral() } } @@ -766,8 +773,8 @@ class RevapiSpec extends IntegrationSpec { """.stripIndent() then: - runTasksSuccessfully('revapi').wasExecuted('revapiAnalyze') - runTasksSuccessfully('revapi').wasUpToDate('revapiAnalyze') + assert runTasksSuccessfully('revapi').task(':revapiAnalyze').outcome == TaskOutcome.SUCCESS + assert runTasksSuccessfully('revapi').task(':revapiAnalyze').outcome == TaskOutcome.UP_TO_DATE } def 'is not up to date when public (not private) api has changed'() { @@ -796,7 +803,7 @@ class RevapiSpec extends IntegrationSpec { '''.stripIndent() then: - runTasksSuccessfully('revapi').wasExecuted('revapiAnalyze') + assert runTasksSuccessfully('revapi').task(':revapiAnalyze').outcome == TaskOutcome.SUCCESS writeToFile javaFile, ''' public class Foo { @@ -805,7 +812,7 @@ class RevapiSpec extends IntegrationSpec { } '''.stripIndent() - runTasksSuccessfully('revapi').wasUpToDate('revapiAnalyze') + assert runTasksSuccessfully('revapi').task(':revapiAnalyze').outcome == TaskOutcome.UP_TO_DATE writeToFile javaFile, ''' public class Foo { @@ -814,7 +821,7 @@ class RevapiSpec extends IntegrationSpec { } '''.stripIndent() - runTasksSuccessfully('revapi').wasExecuted('revapiAnalyze') + assert runTasksSuccessfully('revapi').task(':revapiAnalyze').outcome == TaskOutcome.SUCCESS } def 'compatible with gradle-shadow-jar'() { @@ -847,12 +854,12 @@ class RevapiSpec extends IntegrationSpec { '''.stripIndent() and: - println runTasksSuccessfully('publish').standardOutput + println runTasksSuccessfully('publish').output file(shadowedClass).delete() then: - println runTasksSuccessfully('revapi').standardOutput + println runTasksSuccessfully('revapi').output } def 'changing a protected method in an immutables class is not a break'() { @@ -965,16 +972,15 @@ class RevapiSpec extends IntegrationSpec { writeOutImmutablesClass { it.oldText } and: - runTasksSuccessfully('publish') + runTasksWithConfigurationCache('publish') writeOutImmutablesClass { it.newText } then: - def executionResult = runTasks('revapi') - println executionResult.standardError - !executionResult.success + def buildResult = runTasksWithFailure('revapi') + println buildResult.output - def errorMessage = executionResult.failure.cause.cause.message + def errorMessage = buildResult.output errorMessage.contains 'There were Java public API/ABI breaks reported by revapi:' for (MethodChange methodChange : methodChanges) { @@ -1184,10 +1190,9 @@ class RevapiSpec extends IntegrationSpec { } private String runRevapiExpectingFailure() { - ExecutionResult executionResult = runTasksWithFailure("revapi") - println executionResult.standardOutput - println executionResult.standardError - return executionResult.standardError + BuildResult buildResult = runTasksWithFailure("revapi") + println buildResult.output + return buildResult.output } private void andJunitXmlToHaveBeenProduced(String projectName) { @@ -1196,22 +1201,20 @@ class RevapiSpec extends IntegrationSpec { assert junitOutput.text.contains("java.class.removed") } - @Override - ExecutionResult runTasksSuccessfully(String... tasks) { - ExecutionResult result = runTasks(tasks) - if (result.failure) { - println result.standardOutput - result.rethrowFailure() + private BuildResult runTasksSuccessfully(String... tasks) { + BuildResult result = runTasksWithConfigurationCacheAndCheck(tasks) + if (result.task(':' + tasks[0])?.outcome == TaskOutcome.FAILED) { + println result.output + throw new RuntimeException("Task failed: ${result.output}") } result } - @Override - ExecutionResult runTasksWithFailure(String... tasks) { - ExecutionResult result = runTasks(tasks) - if (result.success) { - println result.standardOutput - assert false + private BuildResult runTasksWithFailure(String... tasks) { + BuildResult result = runTasksAndFailWithConfigurationCache(tasks) + if (result.task(':' + tasks[0])?.outcome == TaskOutcome.SUCCESS) { + println result.output + throw new RuntimeException("Task succeded: ${result.output}") } result } diff --git a/versions.lock b/versions.lock index 10342302..3e515828 100644 --- a/versions.lock +++ b/versions.lock @@ -4,17 +4,16 @@ com.fasterxml.jackson.core:jackson-core:2.14.2 (3 constraints: 55402771) com.fasterxml.jackson.core:jackson-databind:2.14.2 (3 constraints: 0733c1e3) com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2 (1 constraints: 3b053d3b) com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.2 (1 constraints: 3b053d3b) -com.google.code.findbugs:jsr305:3.0.2 (1 constraints: 170aecb4) -com.google.errorprone:error_prone_annotations:2.26.1 (1 constraints: 4d0a46bf) -com.google.guava:failureaccess:1.0.2 (1 constraints: 150ae2b4) -com.google.guava:guava:33.2.1-jre (1 constraints: a9067053) +com.google.errorprone:error_prone_annotations:2.36.0 (1 constraints: 4d0a49bf) +com.google.guava:failureaccess:1.0.3 (1 constraints: 160ae3b4) +com.google.guava:guava:33.4.8-jre (2 constraints: da1e577d) com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) com.google.j2objc:j2objc-annotations:3.0.0 (1 constraints: 150aeab4) -org.checkerframework:checker-qual:3.42.0 (1 constraints: 4b0a47bf) org.freemarker:freemarker:2.3.33 (2 constraints: 5e13e559) org.immutables:serial:2.10.1 (1 constraints: 3605303b) org.immutables:value:2.10.1 (1 constraints: 3605303b) org.jboss:jboss-dmr:1.2.0.Final (1 constraints: 710a5ab9) +org.jspecify:jspecify:1.0.0 (1 constraints: 130ae0b4) org.revapi:revapi:0.11.1 (4 constraints: 8532f166) org.revapi:revapi-basic-features:0.8.1 (1 constraints: 0b050436) org.revapi:revapi-java:0.19.1 (1 constraints: 3d053f3b) @@ -25,25 +24,28 @@ org.yaml:snakeyaml:1.33 (1 constraints: 6f17f827) [Test dependencies] cglib:cglib-nodep:3.2.2 (1 constraints: 490ded24) -com.netflix.nebula:nebula-test:10.0.0 (1 constraints: 3305273b) -junit:junit:4.13.2 (2 constraints: 6a1ed87b) +com.netflix.nebula:nebula-test:10.6.2 (2 constraints: d61de72d) +com.palantir.gradle.plugintesting:configuration-cache-spec:0.10.0 (1 constraints: 3305233b) +com.palantir.gradle.plugintesting:plugin-testing-core:0.10.0 (1 constraints: 3305233b) +junit:junit:4.13.2 (2 constraints: ca1e3ab9) net.bytebuddy:byte-buddy:1.14.16 (2 constraints: f8167267) net.bytebuddy:byte-buddy-agent:1.14.15 (1 constraints: 760bb5e9) -org.apiguardian:apiguardian-api:1.1.2 (6 constraints: 896455cc) +org.apiguardian:apiguardian-api:1.1.2 (7 constraints: cc761de9) org.assertj:assertj-core:3.26.0 (1 constraints: 3d054b3b) -org.codehaus.groovy:groovy:3.0.6 (2 constraints: 1e1b476d) +org.codehaus.groovy:groovy:3.0.25 (3 constraints: 14347add) org.hamcrest:hamcrest:2.2 (1 constraints: d20cdc04) org.hamcrest:hamcrest-core:1.3 (1 constraints: cc05fe3f) -org.junit:junit-bom:5.10.2 (1 constraints: 3a05433b) -org.junit.jupiter:junit-jupiter:5.10.2 (1 constraints: 3f09b79e) -org.junit.jupiter:junit-jupiter-api:5.10.2 (4 constraints: 2f393ee2) -org.junit.jupiter:junit-jupiter-engine:5.10.2 (2 constraints: 7317c263) -org.junit.jupiter:junit-jupiter-params:5.10.2 (2 constraints: 7317c263) -org.junit.platform:junit-platform-commons:1.10.2 (3 constraints: 692ae28e) -org.junit.platform:junit-platform-engine:1.10.2 (4 constraints: ea379e38) -org.junit.vintage:junit-vintage-engine:5.10.2 (1 constraints: 3f09b79e) +org.junit:junit-bom:5.13.4 (1 constraints: 3f054e3b) +org.junit.jupiter:junit-jupiter:5.13.4 (2 constraints: 540ebf67) +org.junit.jupiter:junit-jupiter-api:5.13.4 (5 constraints: 01507ab5) +org.junit.jupiter:junit-jupiter-engine:5.13.4 (2 constraints: 7d17a564) +org.junit.jupiter:junit-jupiter-params:5.13.4 (3 constraints: 3b2e71aa) +org.junit.platform:junit-platform-commons:1.13.4 (3 constraints: 782ad891) +org.junit.platform:junit-platform-engine:1.13.4 (5 constraints: 734a41d4) +org.junit.platform:junit-platform-launcher:1.13.4 (1 constraints: 4009aa9e) +org.junit.vintage:junit-vintage-engine:5.13.4 (1 constraints: 4409c29e) org.mockito:mockito-core:5.12.0 (1 constraints: 3a05473b) org.objenesis:objenesis:3.3 (2 constraints: 9b17f557) org.opentest4j:opentest4j:1.3.0 (2 constraints: cf209249) -org.spockframework:spock-core:2.0-M4-groovy-3.0 (2 constraints: e822d65a) -org.spockframework:spock-junit4:2.0-M4-groovy-3.0 (1 constraints: 25115ddf) +org.spockframework:spock-core:2.3-groovy-3.0 (3 constraints: 2c3deb38) +org.spockframework:spock-junit4:2.3-groovy-3.0 (1 constraints: 7a1000b0) diff --git a/versions.props b/versions.props index 5936b298..b60c40bd 100644 --- a/versions.props +++ b/versions.props @@ -1,6 +1,6 @@ com.fasterxml.jackson.core:jackson-databind = 2.14.2 -com.netflix.nebula:nebula-test = 10.0.0 -org.junit:junit-bom = 5.10.2 +com.netflix.nebula:nebula-test = 10.6.2 +org.junit:junit-bom = 5.13.4 org.revapi:revapi-basic-features = 0.8.1 org.revapi:revapi-java = 0.19.1 org.revapi:revapi-reporter-text = 0.10.1