diff --git a/maven-release-api/pom.xml b/maven-release-api/pom.xml index a801ea9be..4e2c068ec 100644 --- a/maven-release-api/pom.xml +++ b/maven-release-api/pom.xml @@ -52,6 +52,10 @@ org.apache.maven maven-settings + + org.apache.maven.scm + maven-scm-api + org.eclipse.aether aether-util diff --git a/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java b/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java index 312584da4..e2752de97 100644 --- a/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java +++ b/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java @@ -417,6 +417,13 @@ public interface ReleaseDescriptor */ String getProjectVersionPolicyId(); + /** + * Get the (optional) config for the VersionPolicy implementation used to calculate the project versions. + * + * @return String + */ + String getProjectVersionPolicyConfig(); + /** * Get the role-hint for the release Strategy implementation. * diff --git a/maven-release-api/src/main/java/org/apache/maven/shared/release/policy/version/VersionPolicyRequest.java b/maven-release-api/src/main/java/org/apache/maven/shared/release/policy/version/VersionPolicyRequest.java index 4001b8714..9c771089f 100644 --- a/maven-release-api/src/main/java/org/apache/maven/shared/release/policy/version/VersionPolicyRequest.java +++ b/maven-release-api/src/main/java/org/apache/maven/shared/release/policy/version/VersionPolicyRequest.java @@ -20,6 +20,8 @@ */ import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.scm.provider.ScmProvider; +import org.apache.maven.scm.repository.ScmRepository; /** *

VersionPolicyRequest class.

@@ -33,6 +35,12 @@ public class VersionPolicyRequest private Metadata metaData; + private ScmRepository scmRepository; + private ScmProvider scmProvider; + private String workingDirectory; + + private String config; + /** *

Getter for the field version.

* @@ -76,5 +84,93 @@ public VersionPolicyRequest setMetaData( Metadata metaData ) this.metaData = metaData; return this; } - + + /** + *

Getter for the field scmRepository.

+ * + * @return a {@link ScmRepository} object + */ + public ScmRepository getScmRepository() + { + return scmRepository; + } + + /** + *

Setter for the field scmRepository.

+ * + * @param scmRepository The {@link ScmRepository} where the history can be retrieved. + * @return a {@link org.apache.maven.shared.release.policy.version.VersionPolicyRequest} object + */ + public VersionPolicyRequest setScmRepository( ScmRepository scmRepository ) + { + this.scmRepository = scmRepository; + return this; + } + + /** + *

Getter for the field scmProvider.

+ * + * @return a {@link ScmProvider} object + */ + public ScmProvider getScmProvider() + { + return scmProvider; + } + + /** + *

Setter for the field scmProvider.

+ * + * @param scmProvider The {@link ScmProvider} where the history can be retrieved. + * @return a {@link org.apache.maven.shared.release.policy.version.VersionPolicyRequest} object + */ + public VersionPolicyRequest setScmProvider( ScmProvider scmProvider ) + { + this.scmProvider = scmProvider; + return this; + } + + /** + *

Getter for the field workingDirectory.

+ * + * @return the {@link String} that contains the workingDirectory (can be null or empty). + */ + public String getWorkingDirectory() + { + return workingDirectory; + } + + /** + *

Setter for the field workingDirectory.

+ * + * @param workingDirectory The {@link String} that contains the workingDirectory (can be null or empty). + * @return a {@link org.apache.maven.shared.release.policy.version.VersionPolicyRequest} object + */ + public VersionPolicyRequest setWorkingDirectory( String workingDirectory ) + { + this.workingDirectory = workingDirectory; + return this; + } + + /** + *

Getter for the field config.

+ * + * @return the {@link String} that contains the config (can be null or empty). + */ + public String getConfig() + { + return config; + } + + /** + *

Setter for the field config.

+ * + * @param config The {@link String} that contains the config (can be null or empty). + * @return a {@link org.apache.maven.shared.release.policy.version.VersionPolicyRequest} object + */ + public VersionPolicyRequest setConfig( String config ) + { + this.config = config; + return this; + } + } diff --git a/maven-release-api/src/main/java/org/apache/maven/shared/release/versions/VersionParseException.java b/maven-release-api/src/main/java/org/apache/maven/shared/release/versions/VersionParseException.java index 8d24cb112..010c8c4b3 100644 --- a/maven-release-api/src/main/java/org/apache/maven/shared/release/versions/VersionParseException.java +++ b/maven-release-api/src/main/java/org/apache/maven/shared/release/versions/VersionParseException.java @@ -34,4 +34,14 @@ public VersionParseException( String message ) { super( message ); } + + /** + *

Constructor for VersionParseException.

+ * + * @param message a {@link java.lang.String} object + */ + public VersionParseException( String message, Exception e ) + { + super( message, e ); + } } diff --git a/maven-release-manager/pom.xml b/maven-release-manager/pom.xml index 6c0c87ac9..628c03f67 100644 --- a/maven-release-manager/pom.xml +++ b/maven-release-manager/pom.xml @@ -35,7 +35,7 @@ org.apache.maven.release maven-release-api - 3.0.0-M6-SNAPSHOT + ${project.version} org.codehaus.plexus diff --git a/maven-release-manager/src/main/components-fragment.xml b/maven-release-manager/src/main/components-fragment.xml index 3a3668253..b5476f727 100644 --- a/maven-release-manager/src/main/components-fragment.xml +++ b/maven-release-manager/src/main/components-fragment.xml @@ -109,6 +109,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator + @@ -127,6 +131,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator + @@ -146,6 +154,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator + diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java index 3033a45de..3c9ecb889 100644 --- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java +++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java @@ -267,6 +267,10 @@ public void write( BuilderReleaseDescriptor config, File file ) { properties.setProperty( "projectVersionPolicyId", config.getProjectVersionPolicyId() ); } + if ( config.getProjectVersionPolicyConfig() != null ) + { + properties.setProperty( "projectVersionPolicyConfig", config.getProjectVersionPolicyConfig() ); + } if ( config.getProjectNamingPolicyId() != null ) { properties.setProperty( "projectNamingPolicyId", config.getProjectNamingPolicyId() ); diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java index a59ea3278..926922b61 100644 --- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java +++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java @@ -354,6 +354,18 @@ public ReleaseDescriptorBuilder setProjectVersionPolicyId( String projectVersion return this; } + /** + *

setProjectVersionPolicyConfig.

+ * + * @param setProjectVersionPolicyConfig a {@link java.lang.String} object + * @return a {@link org.apache.maven.shared.release.config.ReleaseDescriptorBuilder} object + */ + public ReleaseDescriptorBuilder setProjectVersionPolicyConfig( String setProjectVersionPolicyConfig ) + { + releaseDescriptor.setProjectVersionPolicyConfig( setProjectVersionPolicyConfig ); + return this; + } + /** *

setPushChanges.

* diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java index 8ede3e5ea..c826839e1 100644 --- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java +++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java @@ -156,6 +156,10 @@ public static void copyPropertiesToReleaseDescriptor( Properties properties, Rel { builder.setProjectVersionPolicyId( properties.getProperty( "projectVersionPolicyId" ) ); } + if ( properties.containsKey( "projectVersionPolicyConfig" ) ) + { + builder.setProjectVersionPolicyConfig( properties.getProperty( "projectVersionPolicyConfig" ) ); + } if ( properties.containsKey( "projectNamingPolicyId" ) ) { builder.setProjectNamingPolicyId( properties.getProperty( "projectNamingPolicyId" ) ); diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/MapVersionsPhase.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/MapVersionsPhase.java index 3d405ebc8..0c6425965 100644 --- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/MapVersionsPhase.java +++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/MapVersionsPhase.java @@ -27,6 +27,11 @@ import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.project.MavenProject; +import org.apache.maven.scm.manager.NoSuchScmProviderException; +import org.apache.maven.scm.provider.ScmProvider; +import org.apache.maven.scm.repository.ScmRepository; +import org.apache.maven.scm.repository.ScmRepositoryException; +import org.apache.maven.settings.Settings; import org.apache.maven.shared.release.ReleaseExecutionException; import org.apache.maven.shared.release.ReleaseResult; import org.apache.maven.shared.release.config.ReleaseDescriptor; @@ -34,8 +39,11 @@ import org.apache.maven.shared.release.policy.PolicyException; import org.apache.maven.shared.release.policy.version.VersionPolicy; import org.apache.maven.shared.release.policy.version.VersionPolicyRequest; +import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator; import org.apache.maven.shared.release.util.ReleaseUtil; import org.apache.maven.shared.release.versions.VersionParseException; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.components.interactivity.Prompter; import org.codehaus.plexus.components.interactivity.PrompterException; import org.codehaus.plexus.util.StringUtils; @@ -67,11 +75,18 @@ * @author Brett Porter * @author Robert Scholte */ +@Component( role = ReleasePhase.class, hint = "map-release-versions" ) public class MapVersionsPhase extends AbstractReleasePhase { private ResourceBundle resourceBundle; + /** + * Tool that gets a configured SCM repository from release configuration. + */ + @Requirement + private ScmRepositoryConfigurator scmRepositoryConfigurator; + /** * Whether to convert to a snapshot or a release. */ @@ -261,14 +276,14 @@ else if ( releaseDescriptor.isBranchCreation() ) try { suggestedVersion = - resolveSuggestedVersion( baseVersion, releaseDescriptor.getProjectVersionPolicyId() ); + resolveSuggestedVersion( baseVersion, releaseDescriptor ); } catch ( VersionParseException e ) { if ( releaseDescriptor.isInteractive() ) { suggestedVersion = - resolveSuggestedVersion( "1.0", releaseDescriptor.getProjectVersionPolicyId() ); + resolveSuggestedVersion( "1.0", releaseDescriptor ); } else { @@ -316,9 +331,10 @@ else if ( convertToSnapshot ) return nextVersion; } - private String resolveSuggestedVersion( String baseVersion, String policyId ) + private String resolveSuggestedVersion( String baseVersion, ReleaseDescriptor releaseDescriptor ) throws PolicyException, VersionParseException { + String policyId = releaseDescriptor.getProjectVersionPolicyId(); VersionPolicy policy = versionPolicies.get( policyId ); if ( policy == null ) { @@ -326,6 +342,28 @@ private String resolveSuggestedVersion( String baseVersion, String policyId ) } VersionPolicyRequest request = new VersionPolicyRequest().setVersion( baseVersion ); + + request.setConfig( releaseDescriptor.getProjectVersionPolicyConfig() ); + request.setWorkingDirectory( releaseDescriptor.getWorkingDirectory() ); + + if ( scmRepositoryConfigurator != null && releaseDescriptor.getScmSourceUrl() != null ) + { + try + { + ScmRepository repository = scmRepositoryConfigurator + .getConfiguredRepository( releaseDescriptor, new Settings() ); + + ScmProvider provider = scmRepositoryConfigurator + .getRepositoryProvider( repository ); + + request.setScmRepository( repository ); + request.setScmProvider( provider ); + } + catch ( ScmRepositoryException | NoSuchScmProviderException e ) + { + getLogger().warn( "Next Version will NOT be based on the version control: " + e.getMessage() ); + } + } return convertToSnapshot ? policy.getDevelopmentVersion( request ).getVersion() : policy.getReleaseVersion( request ).getVersion(); } diff --git a/maven-release-manager/src/main/mdo/release-descriptor.mdo b/maven-release-manager/src/main/mdo/release-descriptor.mdo index 723870055..fe236bc10 100644 --- a/maven-release-manager/src/main/mdo/release-descriptor.mdo +++ b/maven-release-manager/src/main/mdo/release-descriptor.mdo @@ -478,6 +478,15 @@ The role-hint for the VersionPolicy implementation used to calculate the project versions. + + projectVersionPolicyConfig + 3.0.0+ + String + + The optional config string for the VersionPolicy implementation used to calculate the project versions. + The format of this string depends on the specific VersionPolicy that is used. + + projectNamingPolicyId 3.0.0+ diff --git a/maven-release-manager/src/test/resources/org/apache/maven/shared/release/phase/MapVersionsPhaseTest.xml b/maven-release-manager/src/test/resources/org/apache/maven/shared/release/phase/MapVersionsPhaseTest.xml index 845cd87a4..2fe86ee6a 100644 --- a/maven-release-manager/src/test/resources/org/apache/maven/shared/release/phase/MapVersionsPhaseTest.xml +++ b/maven-release-manager/src/test/resources/org/apache/maven/shared/release/phase/MapVersionsPhaseTest.xml @@ -31,6 +31,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator +
@@ -45,6 +49,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator + @@ -60,6 +68,10 @@ org.apache.maven.shared.release.policy.version.VersionPolicy versionPolicies + + org.apache.maven.shared.release.scm.ScmRepositoryConfigurator + scmRepositoryConfigurator + diff --git a/maven-release-plugin/pom.xml b/maven-release-plugin/pom.xml index c087faaba..6ebe3ebce 100644 --- a/maven-release-plugin/pom.xml +++ b/maven-release-plugin/pom.xml @@ -54,7 +54,7 @@ org.apache.maven.release maven-release-manager - 3.0.0-M6-SNAPSHOT + ${project.version} org.apache.maven @@ -85,16 +85,22 @@ org.apache.maven.release maven-release-oddeven-policy - 3.0.0-M6-SNAPSHOT + ${project.version} true org.apache.maven.release maven-release-semver-policy - 3.0.0-M6-SNAPSHOT + ${project.version} true - + + org.apache.maven.release + maven-release-ccsemver-policy + ${project.version} + true + + junit junit diff --git a/maven-release-plugin/src/it/projects/branch/MRELEASE-694/pom.xml b/maven-release-plugin/src/it/projects/branch/MRELEASE-694/pom.xml index e84c5ea17..96886a6b8 100644 --- a/maven-release-plugin/src/it/projects/branch/MRELEASE-694/pom.xml +++ b/maven-release-plugin/src/it/projects/branch/MRELEASE-694/pom.xml @@ -54,7 +54,7 @@ org.apache.maven.plugins maven-release-plugin - 2.4.2 + @project.version@ true true diff --git a/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/pom.xml b/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/pom.xml new file mode 100644 index 000000000..5384a26a1 --- /dev/null +++ b/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/pom.xml @@ -0,0 +1,87 @@ + + + + 4.0.0 + org.apache.maven.plugin.release.it + ccsemver-policy + 1.0-SNAPSHOT + + scm:dummytags|nul + scm:dummytags|nul + + + + + org.apache.maven.plugins + maven-release-plugin + @project.version@ + + true + true + false + false + + ci(Release): + ci(Release): Version @{releaseLabel} + v@{project.version} + + CCSemVerVersionPolicy + + + + + + + + + + + + ^v([0-9]+(?:\.[0-9]+(?:\.[0-9]+)?)?)$ + + ^[a-zA-Z]+!(?:\([a-zA-Z0-9_-]+\))?: .*$ + ^BREAKING CHANGE:.*$ + + + ^feat(?:\([a-zA-Z0-9_-]+\))?: .*$ + + + ]]> + + + + + + org.apache.maven.its.release + maven-scm-provider-dummy + 1.0 + + + org.apache.maven.release + maven-release-ccsemver-policy + @project.version@ + + + + + + diff --git a/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/verify.groovy b/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/verify.groovy new file mode 100644 index 000000000..476b00fec --- /dev/null +++ b/maven-release-plugin/src/it/projects/prepare/ccsemver-policy/verify.groovy @@ -0,0 +1,39 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.exists() +assert 1 == buildLog.getText().count("[INFO] From SCM tag with version 1.2.3 doing a MINOR version increase based on commit messages to version 1.3.0") +assert 1 == buildLog.getText().count("[INFO] Full run would be commit 1 files with message: 'ci(Release): Version v1.3.0'") + +// The pom based version is NOT related to what the actual version will be. +File pomXml = new File( basedir, 'pom.xml' ) +assert pomXml.exists() +assert new XmlSlurper().parse( pomXml ).version.text() == "1.0-SNAPSHOT" + +// The actual version is based upon the tags and commit messages. +File pomXmlTag = new File( basedir, 'pom.xml.tag' ) +assert pomXmlTag.exists() +assert new XmlSlurper().parse( pomXmlTag ).version.text() == "1.3.0" + +// The next development version should be standard +File pomXmlNext = new File( basedir, 'pom.xml.next' ) +assert pomXmlNext.exists() +assert new XmlSlurper().parse( pomXmlNext ).version.text() == "1.3.1-SNAPSHOT" diff --git a/maven-release-plugin/src/it/projects/prepare/semver-policy/pom.xml b/maven-release-plugin/src/it/projects/prepare/semver-policy/pom.xml index df9eec731..53059f7ab 100644 --- a/maven-release-plugin/src/it/projects/prepare/semver-policy/pom.xml +++ b/maven-release-plugin/src/it/projects/prepare/semver-policy/pom.xml @@ -21,7 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 org.apache.maven.plugin.release.it - oddversion-policy + semver-policy 1.0-SNAPSHOT scm:dummy|nul diff --git a/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/pom.xml b/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/pom.xml index 9cfbe221f..1b9f6a113 100644 --- a/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/pom.xml +++ b/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/pom.xml @@ -32,7 +32,7 @@ org.apache.maven.scm maven-scm-api - 1.1 + @scmVersion@ diff --git a/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/src/main/java/org/apache/maven/scm/provider/dummy/DummyTagsScmProvider.java b/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/src/main/java/org/apache/maven/scm/provider/dummy/DummyTagsScmProvider.java new file mode 100644 index 000000000..ccae94a12 --- /dev/null +++ b/maven-release-plugin/src/it/setup/maven-scm-provider-dummy/src/main/java/org/apache/maven/scm/provider/dummy/DummyTagsScmProvider.java @@ -0,0 +1,110 @@ +package org.apache.maven.scm.provider.dummy; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.scm.ChangeSet; +import org.apache.maven.scm.CommandParameters; +import org.apache.maven.scm.ScmFileSet; +import org.apache.maven.scm.ScmResult; +import org.apache.maven.scm.command.changelog.ChangeLogScmRequest; +import org.apache.maven.scm.command.changelog.ChangeLogScmResult; +import org.apache.maven.scm.command.changelog.ChangeLogSet; +import org.apache.maven.scm.command.status.StatusScmResult; +import org.apache.maven.scm.provider.AbstractScmProvider; +import org.apache.maven.scm.provider.ScmProviderRepository; + +import java.util.Arrays; +import java.util.Date; + +/** + * A dummy SCM provider used to provide commit messages and tags for testing the Conventional Commits Version Policy. + * + * @plexus.component role="org.apache.maven.scm.provider.ScmProvider" role-hint="dummytags" + * @author Niels Basjes + */ +public class DummyTagsScmProvider + extends AbstractScmProvider +{ + + public String getScmType() + { + return "dummytags"; + } + + public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter ) + { + return new DummyScmProviderRepository(); + } + + @Override + protected StatusScmResult status( ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters ) + { + return new StatusScmResult( "", "", "", true ); + } + + private ChangeSet changeSet(String comment, String... tags) + { + ChangeSet changeSet = new ChangeSet(); + changeSet.setComment( comment ); + changeSet.setAuthor( "Someone " ); + changeSet.setTags( Arrays.asList( tags ) ); + return changeSet; + } + + @Override + public ChangeLogScmResult changeLog(ChangeLogScmRequest request) + { + Date from = new Date( 39817800000L ); + Date to = new Date( 1644768534785L ); + ChangeLogSet changeLogSet = new ChangeLogSet( + Arrays.asList( + changeSet( "Commit 19" ), + changeSet( "Commit 18" ), + changeSet( "Commit 17" ), + changeSet( "Commit 16" ), + changeSet( "Commit 15", "tag 1", "tag 2" ), + changeSet( "feat(it): This is a new feature." ), // For Conventional Commits. + changeSet( "Commit 13" ), + changeSet( "Commit 12", "tag 3" ), + changeSet( "Commit 11" ), + changeSet( "Commit 10" ), + changeSet( "Commit 9" ), + changeSet( "Commit 8" ), + changeSet( "Commit 7" ), + changeSet( "Commit 6", "tag 4" ), + changeSet( "Commit 5" ), + changeSet( "Commit 4" ), + changeSet( "Commit 3" ), + changeSet( "Commit 2", "v1.2.3" ), // For Conventional Commits. + changeSet( "Commit 1" ), + changeSet( "Commit 0" ) + ), from, to + ); + + ScmResult scmResult = new ScmResult( + "No command", + "Special for CCSemVer testing", + "No command output", + true + ); + return new ChangeLogScmResult( changeLogSet, scmResult ); + } + +} diff --git a/maven-release-plugin/src/it/setup/maven-scm-provider-stub/pom.xml b/maven-release-plugin/src/it/setup/maven-scm-provider-stub/pom.xml index 26cef6f39..f5c3ff488 100644 --- a/maven-release-plugin/src/it/setup/maven-scm-provider-stub/pom.xml +++ b/maven-release-plugin/src/it/setup/maven-scm-provider-stub/pom.xml @@ -34,7 +34,7 @@ org.apache.maven.scm maven-scm-api - 1.1 + @scmVersion@ diff --git a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java index 7e67b80eb..0ffb4e4cc 100644 --- a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java +++ b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java @@ -200,6 +200,14 @@ public class BranchReleaseMojo @Parameter( defaultValue = "default", property = "projectVersionPolicyId" ) private String projectVersionPolicyId; + /** + * Optional config for the VersionPolicy implementation used to calculate the project versions. + * + * @since 3.0.0-M6 + */ + @Parameter( property = "projectVersionPolicyConfig" ) + private String projectVersionPolicyConfig; + /** * The role-hint for the {@link org.apache.maven.shared.release.policy.naming.NamingPolicy} * implementation used to calculate the project names. @@ -260,6 +268,7 @@ public void execute() config.setDefaultDevelopmentVersion( developmentVersion ); config.setSuppressCommitBeforeTagOrBranch( suppressCommitBeforeBranch ); config.setProjectVersionPolicyId( projectVersionPolicyId ); + config.setProjectVersionPolicyConfig( projectVersionPolicyConfig ); config.setProjectNamingPolicyId( projectBranchNamingPolicyId ); config.setScmBranchCommitComment( scmBranchCommitComment ); config.setPinExternals( pinExternals ); diff --git a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java index 5547380d1..610b567a0 100644 --- a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java +++ b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java @@ -237,6 +237,14 @@ public class PrepareReleaseMojo @Parameter( defaultValue = "default", property = "projectVersionPolicyId" ) private String projectVersionPolicyId; + /** + * Optional config for the VersionPolicy implementation used to calculate the project versions. + * + * @since 3.0.0-M6 + */ + @Parameter( property = "projectVersionPolicyConfig" ) + private String projectVersionPolicyConfig; + /** * The role-hint for the {@link org.apache.maven.shared.release.policy.naming.NamingPolicy} * implementation used to calculate the project branch and tag names. @@ -360,6 +368,7 @@ protected void prepareRelease( boolean generateReleasePoms ) config.setSuppressCommitBeforeTagOrBranch( suppressCommitBeforeTag ); config.setWaitBeforeTagging( waitBeforeTagging ); config.setProjectVersionPolicyId( projectVersionPolicyId ); + config.setProjectVersionPolicyConfig( projectVersionPolicyConfig ); config.setProjectNamingPolicyId( projectTagNamingPolicyId ); config.setScmDevelopmentCommitComment( scmDevelopmentCommitComment ); config.setScmReleaseCommitComment( scmReleaseCommitComment ); diff --git a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/UpdateVersionsMojo.java b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/UpdateVersionsMojo.java index 60f657dfd..29f6dfe9d 100644 --- a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/UpdateVersionsMojo.java +++ b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/UpdateVersionsMojo.java @@ -93,6 +93,14 @@ public class UpdateVersionsMojo @Parameter( defaultValue = "default", property = "projectVersionPolicyId" ) private String projectVersionPolicyId; + /** + * Optional config for the VersionPolicy implementation used to calculate the project versions. + * + * @since 3.0.0-M6 + */ + @Parameter( property = "projectVersionPolicyConfig" ) + private String projectVersionPolicyConfig; + @Override public void execute() throws MojoExecutionException, MojoFailureException @@ -104,6 +112,7 @@ public void execute() config.setScmUseEditMode( useEditMode ); config.setUpdateDependencies( updateDependencies ); config.setProjectVersionPolicyId( projectVersionPolicyId ); + config.setProjectVersionPolicyConfig( projectVersionPolicyConfig ); config.addOriginalScmInfo( ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() ), project.getScm() ); diff --git a/maven-release-policies/maven-release-ccsemver-policy/pom.xml b/maven-release-policies/maven-release-ccsemver-policy/pom.xml new file mode 100644 index 000000000..4b586ff20 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/pom.xml @@ -0,0 +1,107 @@ + + + + + 4.0.0 + + + org.apache.maven.release + maven-release + 3.0.0-M6-SNAPSHOT + ../.. + + + maven-release-ccsemver-policy + + Maven Release Conventional Commits SemVer Policy + + A version policy that enforce SemVer format and upgrades minor element for next development version. + + + + + ${project.parent.groupId} + maven-release-api + ${project.parent.version} + + + org.semver + api + 0.9.33 + + + + org.apache.maven.scm + maven-scm-api + ${scmVersion} + + + + org.codehaus.plexus + plexus-component-annotations + true + + + + junit + junit + test + + + + + + + org.codehaus.modello + modello-maven-plugin + + + + xpp3-reader + java + + + + + 3.0.0 + false + true + + src/main/mdo/ccsemver-config.mdo + + + + + + org.codehaus.plexus + plexus-component-metadata + + + process-classes + + generate-metadata + + + + + + + + \ No newline at end of file diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicy.java b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicy.java new file mode 100644 index 000000000..0abba2021 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicy.java @@ -0,0 +1,187 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.scm.ScmException; +import org.apache.maven.shared.release.policy.version.VersionPolicy; +import org.apache.maven.shared.release.policy.version.VersionPolicyRequest; +import org.apache.maven.shared.release.policy.version.VersionPolicyResult; +import org.apache.maven.shared.release.versions.VersionParseException; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.semver.Version; +import org.semver.Version.Element; + +import java.util.List; + +import static org.codehaus.plexus.logging.Logger.LEVEL_DEBUG; + +/** + * Uses SemVer combined with the tags and commit messages to increase the version. + */ +@Component( + role = VersionPolicy.class, + hint = "CCSemVerVersionPolicy", + description = "A VersionPolicy following the SemVer rules and looks at " + + "the commit messages following the Conventional Commits convention." + ) +public class CCSemVerVersionPolicy implements VersionPolicy, LogEnabled +{ + // Default logger in case none was set. + // In normal runs this is overruled, in testing it is not. + protected Logger logger = new ConsoleLogger( LEVEL_DEBUG, "debug" ); + + @Override + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + public VersionPolicyResult getReleaseVersion( VersionPolicyRequest request ) + throws VersionParseException + { + VersionRules versionRules = new VersionRules( request.getConfig() ); + try + { + return getReleaseVersion( + request, + versionRules, + new CommitHistory( request, versionRules ) ); + } + catch ( ScmException e ) + { + throw new VersionParseException( "Unable to obtain the information from the SCM history", e ); + } + } + + public VersionPolicyResult getReleaseVersion( VersionPolicyRequest request, + VersionRules versionRules, + CommitHistory commitHistory ) + throws VersionParseException + { + versionRules.enableLogging( logger ); + + boolean usingTag = false; + + String versionString = request.getVersion(); // The current version in the pom + Version version; + + logger.debug( "--------------------------------------------------------" ); + logger.debug( "Determining next ReleaseVersion" ); + logger.debug( "VersionRules: \n" + versionRules ); + logger.debug( "Pom version : " + versionString ); + + logger.debug( "Commit History : \n" + commitHistory ); + + Element maxElementSinceLastVersionTag = versionRules.getMaxElementSinceLastVersionTag( commitHistory ); + + List commitHistoryTags = commitHistory.getTags(); + if ( commitHistoryTags.size() == 1 ) + { + // Use the latest tag we have + versionString = commitHistoryTags.get( 0 ); + usingTag = true; + logger.debug( "Version from tags : " + versionString ); + } + else + { + logger.debug( "Version from tags : NOT FOUND" ); + } + + if ( maxElementSinceLastVersionTag == null ) + { + logger.debug( "Step from commits : No SCM version tags found" ); + } + else + { + logger.debug( "Step from commits : " + maxElementSinceLastVersionTag.name() ); + } + + try + { + version = Version.parse( versionString ); + } + catch ( IllegalArgumentException e ) + { + throw new VersionParseException( e.getMessage() ); + } + + logger.debug( "Current version : " + version ); + + + // If we have a version from the tag we use that + the calculated update. + // If only have the version from the current pom version with -SNAPSHOT removed. + if ( maxElementSinceLastVersionTag != null ) + { + version = version.next( maxElementSinceLastVersionTag ); + } + + Version releaseVersion = version.toReleaseVersion(); + logger.debug( "Next version : " + releaseVersion ); + logger.debug( "--------------------------------------------------------" ); + + if ( usingTag ) + { + logger.info( "From SCM tag with version " + versionString + + " doing a " + maxElementSinceLastVersionTag + + " version increase based on commit messages to version " + releaseVersion ); + } + else + { + if ( maxElementSinceLastVersionTag == null ) + { + logger.info( "From project.version " + versionString + + " (because we did not find any valid SCM tags) going to version " + releaseVersion + + " (because we did not find any minor/major commit messages)." ); + } + else + { + logger.info( "From project.version " + versionString + + " (because we did not find any valid SCM tags) doing a " + maxElementSinceLastVersionTag + + " version increase based on commit messages to version " + releaseVersion ); + } + } + + VersionPolicyResult result = new VersionPolicyResult(); + result.setVersion( releaseVersion.toString() ); + return result; + } + + public VersionPolicyResult getDevelopmentVersion( VersionPolicyRequest request ) + throws VersionParseException + { + Version version; + try + { + version = Version.parse( request.getVersion() ); + } + catch ( IllegalArgumentException e ) + { + throw new VersionParseException( e.getMessage() ); + } + + version = version.next( Element.PATCH ); + VersionPolicyResult result = new VersionPolicyResult(); + result.setVersion( version + "-SNAPSHOT" ); + return result; + } +} diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CommitHistory.java b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CommitHistory.java new file mode 100644 index 000000000..8445c3b8b --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/CommitHistory.java @@ -0,0 +1,180 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.scm.ChangeSet; +import org.apache.maven.scm.ScmException; +import org.apache.maven.scm.ScmFileSet; +import org.apache.maven.scm.command.changelog.ChangeLogScmRequest; +import org.apache.maven.scm.command.changelog.ChangeLogScmResult; +import org.apache.maven.scm.provider.ScmProvider; +import org.apache.maven.scm.repository.ScmRepository; +import org.apache.maven.shared.release.policy.version.VersionPolicyRequest; +import org.apache.maven.shared.release.versions.VersionParseException; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +/** + * Helper class to manage the commit history of the SCM repository. + */ +public class CommitHistory +{ + private final List changes = new ArrayList<>(); + private final List tags = new ArrayList<>(); + private String string; + + public List getChanges() + { + return changes; + } + + public void addChanges( String change ) + { + this.changes.add( change ); + } + + public void addChanges( List changes ) + { + this.changes.addAll( changes ); + } + + public List getTags() + { + return tags; + } + + public void addTags( List tags ) + { + if ( tags != null ) + { + tags.forEach( this::addTags ); + } + } + + public void addTags( String tag ) + { + if ( tag != null && !tag.isEmpty() ) + { + this.tags.add( tag ); + } + } + + public String getLastVersionTag() + { + if ( tags.size() != 1 ) + { + return null; + } + return tags.get( 0 ); + } + + public CommitHistory() + { + // Default constructor which results in an empty history. + } + + public CommitHistory( VersionPolicyRequest request, VersionRules versionRules ) + throws ScmException, VersionParseException + { + ScmRepository scmRepository = request.getScmRepository(); + ScmProvider scmProvider = request.getScmProvider(); + + ChangeLogScmRequest changeLogRequest = new ChangeLogScmRequest( + scmRepository, + new ScmFileSet( new File( request.getWorkingDirectory() ) ) + ); + + List logLines = new ArrayList<>(); + + int limit = 0; + while ( getTags().isEmpty() ) + { + limit += 100; // Read the repository in incremental steps of 100 + changeLogRequest.setLimit( null ); + changeLogRequest.setLimit( limit ); + changes.clear(); + + ChangeLogScmResult changeLog = scmProvider.changeLog( changeLogRequest ); + + logLines.clear(); + logLines.add( "Commit history:" ); + for ( ChangeSet changeSet : changeLog.getChangeLog().getChangeSets() ) + { + List changeSetTags = changeSet.getTags(); + logLines.add( "-- Comment: \"" + changeSet.getComment() + "\"" ); + logLines.add( " Tags : " + changeSetTags ); + List versionTags = changeSetTags + .stream() + .map( tag -> + { + Matcher matcher = versionRules.getTagPattern().matcher( tag ); + if ( matcher.find() ) + { + return matcher.group( 1 ); + } + return null; + } + ) + .filter( Objects::nonNull ) + .collect( Collectors.toList() ); + + if ( !versionTags.isEmpty() ) + { + // Found the previous release tag + if ( versionTags.size() > 1 ) + { + throw new VersionParseException( "Most recent commit with tags has multiple version tags: " + + versionTags ); + } + logLines.add( "-- Version tags: " + versionTags ); + addTags( versionTags ); + break; // We have the last version tag + } + else + { + addChanges( changeSet.getComment() ); + } + } + if ( changeLog.getChangeLog().getChangeSets().size() < limit ) + { + // Apparently there are simply no more commits. + break; + } + } + + StringBuilder sb = new StringBuilder(); + for ( String logLine: logLines ) + { + sb.append( logLine ).append( '\n' ); + } + string = sb.toString(); + } + + @Override + public String toString() + { + return string; + } +} diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/VersionRules.java b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/VersionRules.java new file mode 100644 index 000000000..f4d083faa --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/main/java/org/apache/maven/shared/release/policy/ccsemver/VersionRules.java @@ -0,0 +1,185 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.shared.release.policy.ccsemver.config.CCSemverConfig; +import org.apache.maven.shared.release.policy.ccsemver.config.io.xpp3.CCSemverConfigXpp3Reader; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.logging.console.ConsoleLogger; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.semver.Version; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.codehaus.plexus.logging.Logger.LEVEL_DEBUG; + +/** + * The set of rules that determine from the commit history what the next version should be. + */ +public class VersionRules implements LogEnabled +{ + // Default logger in case none was set. + // In normal runs this is overruled, in testing it is not. + protected Logger logger = new ConsoleLogger( LEVEL_DEBUG, "debug" ); + + @Override + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + private final Pattern tagPattern; + + private final List majorUpdatePatterns = new ArrayList<>(); + private final List minorUpdatePatterns = new ArrayList<>(); + + public VersionRules( String config ) + { + int patternFlags = Pattern.MULTILINE | Pattern.DOTALL | Pattern.UNIX_LINES; + + // The default assumes then entire tag is what we need + String tagRegex = "^([0-9]+(?:\\.[0-9]+(?:\\.[0-9]+)?)?)$"; + + // https://www.conventionalcommits.org/en/v1.0.0/ + majorUpdatePatterns.add( Pattern.compile( "^[a-zA-Z]+!(?:\\([a-zA-Z0-9_-]+\\))?: .*$", patternFlags ) ); + majorUpdatePatterns.add( Pattern.compile( "^BREAKING CHANGE:.*$", patternFlags ) ); + minorUpdatePatterns.add( Pattern.compile( "^feat(?:\\([a-zA-Z0-9_-]+\\))?: .*$", patternFlags ) ); + + if ( config != null && !config.trim().isEmpty() ) + { + ByteArrayInputStream inputStream = new ByteArrayInputStream( config.getBytes( UTF_8 ) ); + CCSemverConfigXpp3Reader configReader = new CCSemverConfigXpp3Reader(); + try + { + CCSemverConfig semverConfig = configReader.read( inputStream ); + + String semverConfigVersionTag = semverConfig.getVersionTag(); + if ( semverConfigVersionTag != null && ! semverConfigVersionTag.trim().isEmpty() ) + { + tagRegex = semverConfigVersionTag; + } + + if ( !semverConfig.getMajorRules().isEmpty() || !semverConfig.getMinorRules().isEmpty() ) + { + majorUpdatePatterns.clear(); + for ( String majorRule : semverConfig.getMajorRules() ) + { + majorUpdatePatterns.add( Pattern.compile( majorRule, patternFlags ) ); + } + minorUpdatePatterns.clear(); + for ( String minorRule : semverConfig.getMinorRules() ) + { + minorUpdatePatterns.add( Pattern.compile( minorRule, patternFlags ) ); + } + } + } + catch ( IOException | XmlPullParserException e ) + { + throw new IllegalArgumentException( "Unable to load the CCSemverConfig: ", e ); + } + } + tagPattern = Pattern.compile( tagRegex, Pattern.MULTILINE ); + } + + public Version.Element getMaxElementSinceLastVersionTag( CommitHistory commitHistory ) + { + boolean needMinorUpdate = false; + for ( String change : commitHistory.getChanges() ) + { + if ( isMajorUpdate( change ) ) + { + logger.debug( "MAJOR: \"" + change + "\"" ); + return Version.Element.MAJOR; + } + else + if ( isMinorUpdate( change ) ) + { + logger.debug( "MINOR: \"" + change + "\"" ); + needMinorUpdate = true; + } + } + + if ( needMinorUpdate ) + { + return Version.Element.MINOR; + } + if ( commitHistory.getLastVersionTag() != null ) + { + logger.debug( "PATCH: Tag " + commitHistory.getLastVersionTag() ); + return Version.Element.PATCH; + } + return null; + } + + public boolean isMajorUpdate( String input ) + { + return matchesAny( majorUpdatePatterns, input ); + } + + public boolean isMinorUpdate( String input ) + { + return matchesAny( minorUpdatePatterns, input ); + } + + private boolean matchesAny( List patterns, String input ) + { + for ( Pattern pattern : patterns ) + { + Matcher matcher = pattern.matcher( input ); + if ( matcher.find() ) + { + return true; + } + } + return false; + } + + public Pattern getTagPattern() + { + return tagPattern; + } + + @Override + public String toString() + { + StringBuilder result = new StringBuilder(); + result.append( "Conventional Commits config:\n" ); + result.append( " VersionTag:\n" ); + result.append( " >>>" ).append( tagPattern ).append( "<<<\n" ); + result.append( " MajorRules:\n" ); + for ( Pattern majorUpdatePattern : majorUpdatePatterns ) + { + result.append( " >>>" ).append( majorUpdatePattern ).append( "<<<\n" ); + } + result.append( " Minor Rules:\n" ); + for ( Pattern minorUpdatePattern : minorUpdatePatterns ) + { + result.append( " >>>" ).append( minorUpdatePattern ).append( "<<<\n" ); + } + return result.toString(); + } +} diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/main/mdo/ccsemver-config.mdo b/maven-release-policies/maven-release-ccsemver-policy/src/main/mdo/ccsemver-config.mdo new file mode 100644 index 000000000..53993fbb1 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/main/mdo/ccsemver-config.mdo @@ -0,0 +1,76 @@ + + + + + ccsemver-config + CCSemverConfig + + The config for the CCSemverVersionPolicy. + + + + package + org.apache.maven.shared.release.policy.ccsemver.config + + + + + CCSemverConfig + 1.0.0+ + + + + versionTag + 1.1.0+ + String + + The regex with exactly 1 capture group that extracts the version from the SCM tag. + + + + minorRules + 1.1.0+ + List + + String + * + + + The list of regexes that must be classified as "minor" version changes. + + + + majorRules + 1.1.0+ + List + + String + * + + + The list of regexes that must be classified as "major" version changes. + + + + + + + diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/site/site.xml b/maven-release-policies/maven-release-ccsemver-policy/src/site/site.xml new file mode 100644 index 000000000..220ecbb57 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/site/site.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicyTest.java b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicyTest.java new file mode 100644 index 000000000..bf88de0a8 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/CCSemVerVersionPolicyTest.java @@ -0,0 +1,152 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.shared.release.policy.version.VersionPolicyRequest; +import org.apache.maven.shared.release.versions.VersionParseException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class CCSemVerVersionPolicyTest +{ + + private void verifyNextVersion( + String versionRulesConfig, + String pomVersion, + String expectedVersion, + String comment, + String... tags + ) throws VersionParseException + { + VersionPolicyRequest request = new VersionPolicyRequest(); + request.setMetaData( null ); + request.setScmProvider( FakeSCM.getSCMProvider( comment, tags ) ); + request.setScmRepository( FakeSCM.getScmRepository() ); + request.setWorkingDirectory( "/tmp" ); + request.setConfig( versionRulesConfig ); + request.setVersion( pomVersion ); + + String suggestedVersion = new CCSemVerVersionPolicy().getReleaseVersion( request ).getVersion(); + + assertEquals( expectedVersion, suggestedVersion ); + } + + private void verifyNextVersionMustFail( + String versionRulesConfig, + String pomVersion, + String comment, + String... tags + ) + { + try + { + verifyNextVersion( versionRulesConfig, pomVersion, "ignore", comment, tags ); + } + catch ( VersionParseException vpe ) + { + // Success ! + return; + } + fail( "Should have failed" ); + } + + @Test + public void testDefaultVersionRules() throws VersionParseException + { + String normal = "Did something"; + String patch = "fix(core): Another fix."; + String minor = "feat(core): New thingy."; + String major = "fix!(core): Breaking improvement"; + + String versionRulesConfig = ""; + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "1.1.1", normal ); // No Tag - No Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "1.1.1", patch ); // No Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "1.2.0", minor ); // No Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "2.0.0", major ); // No Tag - Major Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "2.3.5", normal, "2.3.4", "v3.4.5" ); // Tag - No Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "2.3.5", patch, "2.3.4", "v3.4.5" ); // Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "2.4.0", minor, "2.3.4", "v3.4.5" ); // Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "1.1.1-SNAPSHOT", "3.0.0", major, "2.3.4", "v3.4.5" ); // Tag - Major Comments + + // Too many valid version tags on one commit + verifyNextVersionMustFail( versionRulesConfig, "1.1.1-SNAPSHOT", major, "1.1.1", "2.2.2" ); + } + + @Test + public void testCustomTagPattern() throws VersionParseException { + String normal = "Did something"; + String patch = "fix(core): Another fix."; + String minor = "feat(core): New thingy."; + String major = "fix!(core): Breaking improvement"; + + String versionRulesConfig = "" + + "" + + "^v([0-9]+(?:\\.[0-9]+(?:\\.[0-9]+)?)?)$" + + "" + + ""; + + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.2.2", normal ); // No Tag - No Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.2.2", patch ); // No Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.3.0", minor ); // No Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.0.0", major ); // No Tag - Major Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.4.6", normal, "2.3.4", "v3.4.5" ); // Tag - No Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.4.6", patch, "2.3.4", "v3.4.5" ); // Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.5.0", minor, "2.3.4", "v3.4.5" ); // Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "4.0.0", major, "2.3.4", "v3.4.5" ); // Tag - Major Comments + + // Too many valid version tags on one commit + verifyNextVersionMustFail( versionRulesConfig, "2.2.2-SNAPSHOT", minor, "v1.1.1", "v2.2.2" ); + } + + @Test + public void testCustomVersionRules() throws VersionParseException { + String normal = "This is a different commit."; + String patch = "This is a No Change commit."; + String minor = "This is a Nice Change commit."; + String major = "This is a Big Change commit."; + + String versionRulesConfig = "" + + "" + + "^The awesome ([0-9]+(?:\\.[0-9]+(?:\\.[0-9]+)?)?) release$" + + "" + + "^.*Big Change.*$" + + "" + + "" + + "^.*Nice Change.*$" + + "" + + "" + + ""; + + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.2.2", normal ); // No Tag - No Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.2.2", patch ); // No Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "2.3.0", minor ); // No Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.0.0", major ); // No Tag - Major Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.4.6", normal, "2.3.4", "The awesome 3.4.5 release" ); // Tag - No Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.4.6", patch, "2.3.4", "The awesome 3.4.5 release" ); // Tag - Patch Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "3.5.0", minor, "2.3.4", "The awesome 3.4.5 release" ); // Tag - Minor Comments + verifyNextVersion( versionRulesConfig, "2.2.2-SNAPSHOT", "4.0.0", major, "2.3.4", "The awesome 3.4.5 release" ); // Tag - Major Comments + + // Too many valid version tags on one commit + verifyNextVersionMustFail( versionRulesConfig, "2.2.2-SNAPSHOT", minor, "The awesome 1.1.1 release", "The awesome 2.2.2 release" ); + } + +} diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/FakeSCM.java b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/FakeSCM.java new file mode 100644 index 000000000..f8ccd91ae --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/FakeSCM.java @@ -0,0 +1,117 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.scm.ChangeSet; +import org.apache.maven.scm.ScmResult; +import org.apache.maven.scm.command.changelog.ChangeLogScmRequest; +import org.apache.maven.scm.command.changelog.ChangeLogScmResult; +import org.apache.maven.scm.command.changelog.ChangeLogSet; +import org.apache.maven.scm.provider.AbstractScmProvider; +import org.apache.maven.scm.provider.ScmProvider; +import org.apache.maven.scm.provider.ScmProviderRepository; +import org.apache.maven.scm.repository.ScmRepository; +import org.apache.maven.scm.repository.ScmRepositoryException; + +import java.util.Arrays; +import java.util.Date; + +import static org.junit.Assert.fail; + +public class FakeSCM +{ + public static ScmProvider getSCMProvider( String comment, String... tags ) + { + return new AbstractScmProvider() + { + @Override + public String getScmType() + { + return "dummy"; + } + + @Override + public ScmProviderRepository makeProviderScmRepository( String scmSpecificUrl, char delimiter ) + { + ScmProviderRepository repository = new ScmProviderRepository(){}; + repository.setUser( "someone" ); + repository.setPassword( "secret" ); + repository.setPushChanges( false ); + return repository; + } + + private ChangeSet changeSet(String comment, String... tags) { + ChangeSet changeSet = new ChangeSet(); + changeSet.setComment( comment ); + changeSet.setAuthor( "Someone " ); + changeSet.setTags( Arrays.asList( tags ) ); + return changeSet; + } + + @Override + public ChangeLogScmResult changeLog(ChangeLogScmRequest request) { + Date from = new Date( 39817800000L ); + Date to = new Date( 1644768534785L ); + ChangeLogSet changeLogSet = new ChangeLogSet( + Arrays.asList( + changeSet( "Commit 19" ), + changeSet( "Commit 18" ), + changeSet( "Commit 17" ), + changeSet( "Commit 16" ), + changeSet( "Commit 15", "tag 1", "tag 2" ), + changeSet( comment ), // The comment that should make the difference + changeSet( "Commit 13" ), + changeSet( "Commit 12", "tag 3" ), + changeSet( "Commit 11" ), + changeSet( "Commit 10" ), + changeSet( "Commit 9" ), + changeSet( "Commit 8" ), + changeSet( "Commit 7" ), + changeSet( "Commit 6", "tag 4" ), + changeSet( "Commit 5" ), + changeSet( "Commit 4" ), + changeSet( "Commit 3" ), + changeSet( "Commit 2", tags ), // The first tag that looks like a version + changeSet( "Commit 1" ), + changeSet( "Commit 0" ) + ), from, to + ); + + ScmResult scmResult = new ScmResult( + "No command", + "Special for CCSemVer testing", + "No command output", + true + ); + return new ChangeLogScmResult( changeLogSet, scmResult ); + } + }; + } + + public static ScmRepository getScmRepository() { + try { + return new ScmRepository( "dummy", getSCMProvider("dummy").makeProviderScmRepository("dummy", ':') ); + } catch (ScmRepositoryException e) { + fail(e.getMessage()); + } + return null; // Never reached + } + +} diff --git a/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/NextVersionCalculationTest.java b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/NextVersionCalculationTest.java new file mode 100644 index 000000000..07858af03 --- /dev/null +++ b/maven-release-policies/maven-release-ccsemver-policy/src/test/java/org/apache/maven/shared/release/policy/ccsemver/NextVersionCalculationTest.java @@ -0,0 +1,143 @@ +package org.apache.maven.shared.release.policy.ccsemver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.shared.release.policy.version.VersionPolicyRequest; +import org.apache.maven.shared.release.versions.VersionParseException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.semver.Version; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.semver.Version.Element.MAJOR; +import static org.semver.Version.Element.MINOR; +import static org.semver.Version.Element.PATCH; + +public class NextVersionCalculationTest +{ + + private CCSemVerVersionPolicy versionPolicy; + private static final VersionRules DEFAULT_VERSION_RULES = new VersionRules( null ); + + @Before + public void setUp() + { + versionPolicy = new CCSemVerVersionPolicy(); + } + + @After + public void tearDown() + { + versionPolicy = null; + } + + private void assertNextVersion( VersionRules versionRules, String input, Version.Element element ) + { + switch (element) { + case MAJOR: + assertTrue( versionRules.isMajorUpdate( input ) ); + // We do not care about minor and patch + break; + case MINOR: + assertFalse( versionRules.isMajorUpdate( input ) ); + assertTrue( versionRules.isMinorUpdate( input ) ); + // We do not care about patch + break; + case PATCH: + assertFalse( versionRules.isMajorUpdate( input ) ); + assertFalse( versionRules.isMinorUpdate( input ) ); + break; + } + } + + @Test + public void testMajorMinorPatchDetection() + { + VersionRules rules = DEFAULT_VERSION_RULES; + assertNextVersion( rules, "feat!(core): New feature.", MAJOR ); + assertNextVersion( rules, "feat!: New feature.", MAJOR ); + assertNextVersion( rules, "feat(core): Foo.\n\nBREAKING CHANGE: New feature.\n", MAJOR ); + assertNextVersion( rules, "feat: Foo.\n\nBREAKING CHANGE: New feature.\n", MAJOR ); + + assertNextVersion( rules, "feat(core): New feature.", MINOR ); + assertNextVersion( rules, "feat: New feature.", MINOR ); + + assertNextVersion( rules, "Does not match any pattern.", PATCH ); + } + + @Test + public void testConvertToSnapshot() + throws Exception + { + String suggestedVersion = versionPolicy.getDevelopmentVersion( new VersionPolicyRequest().setVersion( "1.0.0" ) ) + .getVersion(); + + assertEquals( "1.0.1-SNAPSHOT", suggestedVersion ); + } + + public void verifyNextVersion(VersionRules versionRules, + String currentPomVersion, + String tag, + List comments, + String expectedNextVersion) throws VersionParseException { + CommitHistory commitHistory = new CommitHistory(); + commitHistory.addTags( tag ); + commitHistory.addChanges( comments ); + + assertEquals( expectedNextVersion, versionPolicy + .getReleaseVersion( + new VersionPolicyRequest().setVersion( currentPomVersion ), + versionRules, + commitHistory + ).getVersion() ); + } + + String patch1 = "Quick patch"; + String patch2 = "fix(core): Another fix."; + String minor1 = "feat(core): New thingy."; + String major1 = "fix!(core): Breaking improvement" ; + + List EMPTY = Collections.emptyList(); + List MAJOR_MESSAGES = Arrays.asList( patch1, patch2, minor1, major1 ); + List MINOR_MESSAGES = Arrays.asList( patch1, patch2, minor1 ); + List PATCH_MESSAGES = Arrays.asList( patch1, patch2 ); + + @Test + public void testDefaultVersionRules() throws VersionParseException + { + VersionRules rules = DEFAULT_VERSION_RULES; + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "", EMPTY, "1.2.3"); // No Tag - No Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "", PATCH_MESSAGES, "1.2.3"); // No Tag - Patch Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "", MINOR_MESSAGES, "1.3.0"); // No Tag - Minor Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "", MAJOR_MESSAGES, "2.0.0"); // No Tag - Major Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "2.3.4", EMPTY, "2.3.5"); // Tag - No Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "2.3.4", PATCH_MESSAGES, "2.3.5"); // Tag - Patch Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "2.3.4", MINOR_MESSAGES, "2.4.0"); // Tag - Minor Comments + verifyNextVersion( rules, "1.2.3-SNAPSHOT", "2.3.4", MAJOR_MESSAGES, "3.0.0"); // Tag - Major Comments + } + +} diff --git a/pom.xml b/pom.xml index decab804b..5df3de071 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ maven-release-manager maven-release-policies/maven-release-oddeven-policy maven-release-policies/maven-release-semver-policy + maven-release-policies/maven-release-ccsemver-policy maven-release-plugin @@ -87,7 +88,7 @@ - 1.12.2 + 2.0.0-M2-SNAPSHOT 8 3.0 maven-release-archives/maven-release-LATEST @@ -103,6 +104,8 @@ .repository + **/*.iml + **/target/** diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt index 1f0b5da66..d62aa400c 100644 --- a/src/site/apt/index.apt +++ b/src/site/apt/index.apt @@ -42,6 +42,8 @@ Maven Release *----------------------------------------------------------+----------------+ | {{{./maven-release-policies/maven-release-semver-policy/}maven-release-semver-policy}} | a version policy that enforce SemVer format and upgrades minor element for next development version *----------------------------------------------------------+----------------+ +| {{{./maven-release-policies/maven-release-ccsemver-policy/}maven-release-ccsemver-policy}} | a version policy that enforce SemVer format and upgrades major/minor/patch element for next development version depending on the commit messages since the previous release (actually tag in the SCM). +*----------------------------------------------------------+----------------+ Notes for Developers