From 1746be2832d0b31cdb5aaa8c8b9298df3d4f1ff2 Mon Sep 17 00:00:00 2001 From: Sebastian Ratz Date: Fri, 31 May 2024 15:02:38 +0200 Subject: [PATCH] tycho-versions-plugin: Support ci-friendly versions In PomFile#getVersion(), resolve properties used in the raw version of the project. This is necessary so that downstream manipulators (MANIFEST.MF, feature.xml, category.xml, ...) can see the actual version and act on it. Fixes #3744. --- .../ci_friendly/META-INF/MANIFEST.MF | 6 ++ .../set-version/ci_friendly/pom.xml | 23 ++++++ .../TychoVersionsPluginTest.java | 20 +++++ .../versions/manipulation/PomManipulator.java | 26 ++----- .../eclipse/tycho/versions/pom/PomFile.java | 24 ++++-- .../eclipse/tycho/versions/pom/PomUtil.java | 75 +++++++++++++++++++ 6 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/META-INF/MANIFEST.MF create mode 100644 tycho-its/projects/tycho-version-plugin/set-version/ci_friendly/pom.xml create mode 100644 tycho-versions-plugin/src/main/java/org/eclipse/tycho/versions/pom/PomUtil.java 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(); + } +}