diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/gradle/GradleUpdater.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/gradle/GradleUpdater.java index b47ff3d8..c4f3a373 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/gradle/GradleUpdater.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/gradle/GradleUpdater.java @@ -8,10 +8,13 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Scanner; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,22 +40,22 @@ public class GradleUpdater implements ReleaserPropertiesAware { * For the given root folder (typically the working directory) performs the whole * flow of updating {@code gradle.properties} with values from BOM project. * Remember to pass the mapping from a property name inside {@code gradle.properties} to - * the project name via {@link ReleaserProperties.Gradle#gradlePropsSubstitution} + * the project name via {@link ReleaserProperties.Gradle#getGradlePropsSubstitution} * @param projectRoot - root folder with project to update * @param projects - versions of projects used to update poms * @param versionFromBom - version for the project from Spring Cloud Release - * @param assertSnapshots - should snapshots presence be asserted + * @param assertVersions - should snapshots / milestone / rc presence be asserted */ public void updateProjectFromBom(File projectRoot, Projects projects, - ProjectVersion versionFromBom, boolean assertSnapshots) { - processAllGradleProps(projectRoot, projects, versionFromBom, assertSnapshots); + ProjectVersion versionFromBom, boolean assertVersions) { + processAllGradleProps(projectRoot, projects, versionFromBom, assertVersions); } private void processAllGradleProps(File projectRoot, Projects projects, - ProjectVersion versionFromScRelease, boolean assertSnapshots) { + ProjectVersion versionFromScRelease, boolean assertVersions) { try { Files.walkFileTree(projectRoot.toPath(), - new GradlePropertiesWalker(this.properties, projects, versionFromScRelease, assertSnapshots)); + new GradlePropertiesWalker(this.properties, projects, versionFromScRelease, assertVersions)); } catch (IOException e) { throw new IllegalStateException(e); @@ -69,15 +72,18 @@ public class GradleUpdater implements ReleaserPropertiesAware { private final ReleaserProperties properties; private final Projects projects; - private final boolean snapshotVersion; - private final boolean assertSnapshots; + private final boolean skipVersionAssert; + private final boolean assertVersions; + private final List unacceptableVersionPatterns; private GradlePropertiesWalker(ReleaserProperties properties, Projects projects, - ProjectVersion versionFromScRelease, boolean assertSnapshots) { + ProjectVersion versionFromScRelease, boolean assertVersions) { this.properties = properties; this.projects = projects; - this.snapshotVersion = !assertSnapshots || versionFromScRelease.isSnapshot(); - this.assertSnapshots = assertSnapshots; + List unacceptableVersionPatterns = versionFromScRelease.unacceptableVersionPatterns(); + this.unacceptableVersionPatterns = unacceptableVersionPatterns; + this.skipVersionAssert = !assertVersions || unacceptableVersionPatterns.isEmpty(); + this.assertVersions = assertVersions; } @Override @@ -115,20 +121,23 @@ public class GradleUpdater implements ReleaserPropertiesAware { } private void assertNoSnapshotsArePresent(Path path) { - if (this.assertSnapshots && !this.snapshotVersion) { - log.debug("Update is a non-snapshot one. Checking if no snapshot versions remained in the gradle prop"); + if (this.assertVersions && !this.skipVersionAssert) { + log.debug("Update should check if no wrong versions remained in the gradle prop. List of wrong patterns: {}", + this.unacceptableVersionPatterns.stream().map(Pattern::pattern).collect(Collectors + .toList())); Scanner scanner = new Scanner(asString(path)); int lineNumber = 0; while (scanner.hasNextLine()) { String line = scanner.nextLine(); lineNumber++; - boolean containsSnapshot = line.contains("SNAPSHOT"); - if (containsSnapshot) { - throw new IllegalStateException("The file [" + path + "] contains a SNAPSHOT " - + "version for a non snapshot release in line number [" + lineNumber + "]\n\n" + line); + Pattern matchingPattern = this.unacceptableVersionPatterns.stream() + .filter(pattern -> pattern.matcher(line).matches()) + .findFirst().orElse(null); + if (matchingPattern != null) { + throw new IllegalStateException("The file [" + path + "] matches the [ " + matchingPattern.pattern() + "] pattern in line number [" + lineNumber + "]\n\n" + line); } } - log.info("No snapshot versions remained in the pom"); + log.info("No invalid versions remained in the gradle properties"); } } @@ -145,7 +154,7 @@ public class GradleUpdater implements ReleaserPropertiesAware { private boolean pathIgnored(File file) { String path = file.getPath(); - return this.assertSnapshots && + return this.assertVersions && this.properties.getGradle().getIgnoredGradleRegex().stream().anyMatch(path::matches); } diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectPomUpdater.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectPomUpdater.java index f88294d8..842d389b 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectPomUpdater.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectPomUpdater.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.List; import java.util.Scanner; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -99,20 +100,20 @@ public class ProjectPomUpdater implements ReleaserPropertiesAware { * @param projects - versions of projects used to update poms * @param versionFromReleaseTrain - version for the built project taken * from release train (e.g. Spring Cloud Release project) - * @param assertSnapshots - should snapshots present be asserted + * @param assertVersions - should version assertion take place */ public void updateProjectFromReleaseTrain(File projectRoot, Projects projects, - ProjectVersion versionFromReleaseTrain, boolean assertSnapshots) { + ProjectVersion versionFromReleaseTrain, boolean assertVersions) { Versions versions = new Versions(projects, this.properties); if (!this.pomUpdater.shouldProjectBeUpdated(projectRoot, versions)) { log.info("Skipping project updating"); return; } - updatePoms(projectRoot, projects, versionFromReleaseTrain, assertSnapshots); + updatePoms(projectRoot, projects, versionFromReleaseTrain, assertVersions); } private void updatePoms(File projectRoot, Projects projects, - ProjectVersion versionFromScRelease, boolean assertSnapshots) { + ProjectVersion versionFromScRelease, boolean assertVersions) { File rootPom = new File(projectRoot, "pom.xml"); if (!rootPom.exists()) { log.info("No pom.xml present, skipping!"); @@ -120,7 +121,7 @@ public class ProjectPomUpdater implements ReleaserPropertiesAware { } ModelWrapper rootPomModel = this.pomUpdater.readModel(rootPom); processAllPoms(projectRoot, new PomWalker(rootPomModel, projects, this.pomUpdater, - this.properties, versionFromScRelease, assertSnapshots)); + this.properties, versionFromScRelease, assertVersions)); } private void processAllPoms(File projectRoot, PomWalker pomWalker) { @@ -144,18 +145,21 @@ public class ProjectPomUpdater implements ReleaserPropertiesAware { private final Versions versions; private final PomUpdater pomUpdater; private final ReleaserProperties properties; - private final boolean snapshotVersion; - private final boolean assertSnapshots; + private final boolean skipVersionAssert; + private final boolean assertVersions; + private final List unacceptableVersionPatterns; private PomWalker(ModelWrapper rootPom, Projects projects, PomUpdater pomUpdater, ReleaserProperties properties, ProjectVersion versionFromScRelease, - boolean assertSnapshots) { + boolean assertVersions) { this.rootPom = rootPom; this.versions = new Versions(projects, properties); this.pomUpdater = pomUpdater; this.properties = properties; - this.snapshotVersion = !assertSnapshots || versionFromScRelease.isSnapshot(); - this.assertSnapshots = assertSnapshots; + List unacceptableVersionPatterns = versionFromScRelease.unacceptableVersionPatterns(); + this.unacceptableVersionPatterns = unacceptableVersionPatterns; + this.skipVersionAssert = !assertVersions || unacceptableVersionPatterns.isEmpty(); + this.assertVersions = assertVersions; } @Override @@ -168,21 +172,23 @@ public class ProjectPomUpdater implements ReleaserPropertiesAware { } ModelWrapper model = this.pomUpdater.updateModel(this.rootPom, file, this.versions); this.pomUpdater.overwritePomIfDirty(model, this.versions, file); - if (this.assertSnapshots && !this.snapshotVersion && !this.pomUpdater.hasSkipDeployment(model.model)) { + if (this.assertVersions && !this.skipVersionAssert && !this.pomUpdater.hasSkipDeployment(model.model)) { log.debug("Update is a non-snapshot one. Checking if no snapshot versions remained in the pom"); Scanner scanner = new Scanner(asString(path)); int lineNumber = 0; while (scanner.hasNextLine()) { String line = scanner.nextLine(); lineNumber++; - boolean containsSnapshot = line.contains("SNAPSHOT") && - IGNORED_SNAPSHOT_LINE_PATTERNS.stream().noneMatch(line::matches); - if (containsSnapshot) { - throw new IllegalStateException("The file [" + path + "] contains a SNAPSHOT " - + "version for a non snapshot release in line number [" + lineNumber + "]\n\n" + line); + Pattern matchingPattern = this.unacceptableVersionPatterns.stream() + .filter(pattern -> + IGNORED_SNAPSHOT_LINE_PATTERNS.stream().noneMatch(line::matches) && + pattern.matcher(line).matches()) + .findFirst().orElse(null); + if (matchingPattern != null) { + throw new IllegalStateException("The file [" + path + "] matches the [ " + matchingPattern.pattern() + "] pattern in line number [" + lineNumber + "]\n\n" + line); } } - log.info("No snapshot versions remained in the pom"); + log.info("No invalid versions remained in the pom"); } } return FileVisitResult.CONTINUE; @@ -190,7 +196,7 @@ public class ProjectPomUpdater implements ReleaserPropertiesAware { private boolean pathIgnored(File file) { String path = file.getPath(); - return this.assertSnapshots && + return this.assertVersions && this.properties.getPom().getIgnoredPomRegex().stream().anyMatch(path::matches); } diff --git a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java index dc3fd9b8..6a2f9177 100644 --- a/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java +++ b/spring-cloud-release-tools-core/src/main/java/org/springframework/cloud/release/internal/pom/ProjectVersion.java @@ -1,9 +1,14 @@ package org.springframework.cloud.release.internal.pom; import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.regex.Pattern; import org.apache.maven.model.Model; + import org.springframework.util.StringUtils; /** @@ -14,6 +19,9 @@ import org.springframework.util.StringUtils; */ public class ProjectVersion { + private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("^.*\\.(BUILD-)?SNAPSHOT.*$"); + private static final String MILESTONE_REGEX = ".*\\.M[0-9]+"; + private static final String RC_REGEX = "^.*\\.RC.*$"; public final String projectName; public final String version; private final Model model; @@ -134,11 +142,11 @@ public class ProjectVersion { } public boolean isRc() { - return this.version != null && this.version.contains("RC"); + return this.version != null && this.version.matches(RC_REGEX); } public boolean isMilestone() { - return this.version != null && this.version.matches(".*M[0-9]+"); + return this.version != null && this.version.matches(MILESTONE_REGEX); } public boolean isRelease() { @@ -207,6 +215,16 @@ public class ProjectVersion { @Override public int hashCode() { return Objects.hash(this.projectName); } + + public List unacceptableVersionPatterns() { + if (isSnapshot()) { + return Collections.emptyList(); + } else if (isMilestone() || isRc()) { + return Collections.singletonList(SNAPSHOT_PATTERN); + } + // treat like GA + return Arrays.asList(SNAPSHOT_PATTERN, Pattern.compile(MILESTONE_REGEX), Pattern.compile(RC_REGEX)); + } } class VersionNumber implements Comparable { diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/gradle/GradleUpdaterTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/gradle/GradleUpdaterTests.java index 893a9e5a..b5acf760 100644 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/gradle/GradleUpdaterTests.java +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/gradle/GradleUpdaterTests.java @@ -1,8 +1,5 @@ package org.springframework.cloud.release.internal.gradle; -import static org.assertj.core.api.BDDAssertions.then; -import static org.assertj.core.api.BDDAssertions.thenThrownBy; - import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -14,11 +11,15 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; + import org.springframework.cloud.release.internal.ReleaserProperties; import org.springframework.cloud.release.internal.pom.ProjectVersion; import org.springframework.cloud.release.internal.pom.Projects; import org.springframework.util.FileSystemUtils; +import static org.assertj.core.api.BDDAssertions.then; +import static org.assertj.core.api.BDDAssertions.thenThrownBy; + /** * @author Marcin Grzejszczak */ @@ -70,8 +71,8 @@ public class GradleUpdaterTests { ); thenThrownBy(() -> new GradleUpdater(properties).updateProjectFromBom(projectRoot, - projects, new ProjectVersion("spring-cloud-contract", "1.0.0"), true)) - .hasMessageContaining("contains a SNAPSHOT version for a non snapshot release in line number"); + projects, new ProjectVersion("spring-cloud-contract", "1.0.0.RELEASE"), true)) + .hasMessageContaining("matches the [ ^.*\\.(BUILD-)?SNAPSHOT.*$] pattern in line number [1]"); } private File file(String relativePath) throws URISyntaxException { diff --git a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java index 5a1d96e3..8c5c9bb9 100644 --- a/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java +++ b/spring-cloud-release-tools-core/src/test/java/org/springframework/cloud/release/internal/pom/ProjectVersionTests.java @@ -3,6 +3,8 @@ package org.springframework.cloud.release.internal.pom; import java.io.File; import java.net.URI; import java.net.URISyntaxException; +import java.util.List; +import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; @@ -360,6 +362,45 @@ public class ProjectVersionTests { then(projectVersion(thisVersion).compareToReleaseTrainName(thatVersion)).isPositive(); } + @Test + public void should_return_no_unacceptable_patterns_for_a_snapshot_version() { + then(projectVersion("1.0.0.BUILD-SNAPSHOT").unacceptableVersionPatterns()).isEmpty(); + then(projectVersion("1.0.0.SNAPSHOT").unacceptableVersionPatterns()).isEmpty(); + } + + @Test + public void should_return_snapshot_unacceptable_patterns_for_a_milestone_or_rc_version() { + List milestonePatterns = projectVersion("1.0.0.M1").unacceptableVersionPatterns(); + then(milestonePatterns).isNotEmpty(); + then(milestonePatterns.get(0).pattern()).contains("SNAPSHOT"); + + List rcPatterns = projectVersion("1.0.0.RC1").unacceptableVersionPatterns(); + then(rcPatterns).isNotEmpty(); + then(rcPatterns.get(0).pattern()).contains("SNAPSHOT"); + } + + @Test + public void should_return_snapshot_milestone_rc_unacceptable_patterns_for_a_ga_or_sr_version() { + List gaPatterns = projectVersion("1.0.0.RELEASE").unacceptableVersionPatterns(); + thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(gaPatterns); + + gaPatterns = projectVersion("1.0.0").unacceptableVersionPatterns(); + thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(gaPatterns); + + List srPatterns = projectVersion("1.0.0.SR1").unacceptableVersionPatterns(); + thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(srPatterns); + + List unknownTypeOfVersion = projectVersion("1.0.0.SOMETHING").unacceptableVersionPatterns(); + thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(unknownTypeOfVersion); + } + + private void thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(List unknownTypeOfVersion) { + then(unknownTypeOfVersion).isNotEmpty(); + then(unknownTypeOfVersion.get(0).pattern()).contains("SNAPSHOT"); + then(unknownTypeOfVersion.get(1).pattern()).contains("M[0-9]"); + then(unknownTypeOfVersion.get(2).pattern()).contains("RC"); + } + private ProjectVersion projectVersion(String version) { return new ProjectVersion("foo", version); }