diff --git a/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/META-INF/MANIFEST.MF b/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..fbceba0885 --- /dev/null +++ b/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Test Artifact +Bundle-SymbolicName: test.artifact +Bundle-Version: 1.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/pom.xml b/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/pom.xml new file mode 100644 index 0000000000..91ef4d25e2 --- /dev/null +++ b/tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/pom.xml @@ -0,0 +1,23 @@ + + + 4.0.0 + test.artifact + org.tycho.its + eclipse-plugin + + 1.0.0-SNAPSHOT + + ${revision} + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + + + diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/versionsplugin/TychoVersionsPluginTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/versionsplugin/TychoVersionsPluginTest.java index a29635d0b6..c20600c256 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/versionsplugin/TychoVersionsPluginTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/versionsplugin/TychoVersionsPluginTest.java @@ -364,6 +364,26 @@ public void testUpdatePomsOfModularPom() throws Exception { } + @Test + public void testCiFriendlyVersion() throws Exception { + String expectedNewVersion = "2.0.0-SNAPSHOT"; + String expectedNewOSGiVersion = "2.0.0.qualifier"; + + Verifier verifier = getVerifier("tycho-version-plugin/set-version/ci_friendly", false); + + verifier.addCliOption("-DnewVersion=" + expectedNewVersion); + verifier.executeGoal("org.eclipse.tycho:tycho-versions-plugin:" + VERSION + ":set-version"); + + verifier.verifyErrorFreeLog(); + + MavenXpp3Reader pomReader = new MavenXpp3Reader(); + Model pomModel = pomReader.read(new FileReader(new File(verifier.getBasedir(), "pom.xml"))); + assertEquals("${revision}", pomModel.getVersion()); + assertEquals(expectedNewVersion, pomModel.getProperties().getProperty("revision")); + Manifest manifest = getManifest(verifier, "."); + assertEquals(expectedNewOSGiVersion, manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION)); + } + public static File file(Verifier verifier, String... path) { return Path.of(verifier.getBasedir(), path).toFile(); } diff --git a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/manipulation/PomManipulator.java b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/manipulation/PomManipulator.java index b77449b4f7..2dfeedf745 100644 --- a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/manipulation/PomManipulator.java +++ b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/manipulation/PomManipulator.java @@ -11,7 +11,8 @@ * Sonatype Inc. - initial API and implementation * Sebastien Arod - introduce VersionChangesDescriptor * Bachmann electronic GmbH. - #472579 - Support setting the version for pomless builds - * Christoph Läubrich - Bug 550313 - tycho-versions-plugin uses hard-coded polyglot file + * Christoph Läubrich - Bug 550313 - tycho-versions-plugin uses hard-coded polyglot file + * SAP SE - #3744 - ci-friendly version support *******************************************************************************/ package org.eclipse.tycho.versions.manipulation; @@ -20,13 +21,10 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.codehaus.plexus.component.annotations.Component; import org.eclipse.tycho.versions.engine.MetadataManipulator; @@ -40,6 +38,7 @@ import org.eclipse.tycho.versions.pom.Plugin; import org.eclipse.tycho.versions.pom.PluginManagement; import org.eclipse.tycho.versions.pom.PomFile; +import org.eclipse.tycho.versions.pom.PomUtil; import org.eclipse.tycho.versions.pom.Profile; import org.eclipse.tycho.versions.pom.Property; @@ -51,8 +50,6 @@ public class PomManipulator extends AbstractMetadataManipulator { public static final String HINT = POM; - private static final Pattern CI_FRIENDLY_EXPRESSION = Pattern.compile("\\$\\{(.+?)\\}"); - @Override public boolean addMoreChanges(ProjectMetadata project, VersionChangesDescriptor versionChangeContext) { PomFile pom = project.getMetadata(PomFile.class); @@ -116,14 +113,8 @@ public void applyChanges(ProjectMetadata project, VersionChangesDescriptor versi String version = Versions.toMavenVersion(change.getVersion()); String newVersion = Versions.toMavenVersion(change.getNewVersion()); if (isGavEquals(pom, change)) { - String v = pom.getVersion(); - if (isCiFriendly(v)) { - //applyPropertyChange(pom, version, newVersion); - Matcher m = CI_FRIENDLY_EXPRESSION.matcher(v.trim()); - List ciFriendlyProperties = new ArrayList(); - while (m.find()) { - ciFriendlyProperties.add(m.group(1)); - } + List ciFriendlyProperties = PomUtil.getContainedPropertyNames(pom.getRawVersion()); + if (!ciFriendlyProperties.isEmpty()) { if (ciFriendlyProperties.size() == 1) { //thats actually a simply property change applyPropertyChange(pomName, pom, ciFriendlyProperties.get(0), newVersion); @@ -148,7 +139,8 @@ public void applyChanges(ProjectMetadata project, VersionChangesDescriptor versi } } else { GAV parent = pom.getParent(); - if (parent != null && isGavEquals(parent, change) && !isCiFriendly(parent.getVersion())) { + if (parent != null && isGavEquals(parent, change) + && !PomUtil.containsProperties(parent.getVersion())) { logger.info(" %s//project/parent/version: %s => %s".formatted(pomName, version, newVersion)); parent.setVersion(newVersion); } @@ -180,10 +172,6 @@ public void applyChanges(ProjectMetadata project, VersionChangesDescriptor versi } - private boolean isCiFriendly(String v) { - return v != null && v.contains("${"); - } - protected void changeDependencyManagement(String pomPath, DependencyManagement dependencyManagment, PomVersionChange change, String version, String newVersion) { if (dependencyManagment != null) { diff --git a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomFile.java b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomFile.java index 4552718a10..c6e2a3344d 100644 --- a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomFile.java +++ b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomFile.java @@ -10,7 +10,8 @@ * Contributors: * Sonatype Inc. - initial API and implementation * Bachmann electronic GmbH. - #472579 - Support setting the version for pomless builds - * Christoph Läubrich - Bug 550313 - tycho-versions-plugin uses hard-coded polyglot file + * Christoph Läubrich - Bug 550313 - tycho-versions-plugin uses hard-coded polyglot file + * SAP SE - #3744 - ci-friendly version support *******************************************************************************/ package org.eclipse.tycho.versions.pom; @@ -48,8 +49,10 @@ public class PomFile { private Document document; private Element project; - /** The (effective) project version */ + /** The (raw) project version */ private String version; + /** The ${property}-resolved version, in case of ci-friendly versions */ + private String resolvedVersion; private final boolean preferExplicitProjectVersion; private final boolean isMutable; @@ -142,7 +145,7 @@ private static void removeVersionElementFromXML(Element project) { /** * Sets the version in the parent POM declaration. This never affects the (effective) version of * the project itself. - * + * * @see #setVersion(String) */ public void setParentVersion(String newVersion) { @@ -158,13 +161,24 @@ public void setParentVersion(String newVersion) { */ public void setVersion(String version) { this.version = version; + this.resolvedVersion = null; } /** - * Returns the (effective) version of the project. + * Returns the (effective) version of the project with properties resolved. */ public String getVersion() { - return version; + if (this.resolvedVersion == null) { + this.resolvedVersion = PomUtil.expandProperties(version, getProperties()); + } + return this.resolvedVersion; + } + + /** + * Returns the literal version of the project without any properties resolved. + */ + public String getRawVersion() { + return this.version; } public String getPackaging() { diff --git a/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomUtil.java b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomUtil.java new file mode 100644 index 0000000000..fda4d927ad --- /dev/null +++ b/tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomUtil.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2024 SAP SE and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * SAP SE - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.versions.pom; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PomUtil { + + private static final Pattern PROPERTY_PATTERN = Pattern.compile("\\$\\{(.+?)\\}"); + + /** + * Returns whether the string contains properties ${properties}. + */ + public static boolean containsProperties(String str) { + return str != null && str.contains("${"); + } + + /** + * Expands properties in the given string. + *

+ * If a property is not found it is left unexpanded. + * + * @param str + * the input string + * @param properties + * possible replacement properties + * @return the expanded string + */ + public static String expandProperties(String str, List properties) { + if (containsProperties(str)) { + StringBuilder resolvedVersionBuilder = new StringBuilder(); + Matcher m = PROPERTY_PATTERN.matcher(str.trim()); + while (m.find()) { + String unexpandedProperty = m.group(); + String propertyName = m.group(1); + m.appendReplacement(resolvedVersionBuilder, + properties.stream().filter(p -> p.getName().equals(propertyName)).map(p -> p.getValue()) + .findFirst().orElse(unexpandedProperty)); + } + m.appendTail(resolvedVersionBuilder); + return resolvedVersionBuilder.toString(); + } else { + return str; + } + } + + /** + * Returns the list of property names that make up the given string. + */ + public static List getContainedPropertyNames(String str) { + if (containsProperties(str)) { + Matcher m = PROPERTY_PATTERN.matcher(str.trim()); + List propertyNames = new ArrayList<>(); + while (m.find()) { + propertyNames.add(m.group(1)); + } + return Collections.unmodifiableList(propertyNames); + } + return List.of(); + } +}