[MNG-5726] Support regular expression matching in profile activation for (#1431)

OS version

This requires using the reserved prefix "regex:" in the version element.
This commit is contained in:
Konrad Windszus 2024-03-07 16:14:20 +01:00 committed by GitHub
parent 9a84fdf2a9
commit f860a8693d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 415 additions and 31 deletions

View File

@ -32,10 +32,6 @@ under the License.
<description>The effective model builder, with inheritance, profile activation, interpolation, ...</description>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-interpolation</artifactId>

View File

@ -21,12 +21,14 @@ package org.apache.maven.model.profile.activation;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Locale;
import org.apache.maven.model.Activation;
import org.apache.maven.model.ActivationOS;
import org.apache.maven.model.Profile;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.profile.ProfileActivationContext;
import org.codehaus.plexus.util.Os;
import org.apache.maven.utils.Os;
/**
* Determines profile activation based on the operating system of the current runtime platform.
@ -38,6 +40,8 @@ import org.codehaus.plexus.util.Os;
@Singleton
public class OperatingSystemProfileActivator implements ProfileActivator {
private static final String REGEX_PREFIX = "regex:";
@Override
public boolean isActive(Profile profile, ProfileActivationContext context, ModelProblemCollector problems) {
Activation activation = profile.getActivation();
@ -54,17 +58,21 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
boolean active = ensureAtLeastOneNonNull(os);
String actualOsName = context.getSystemProperties().get("os.name").toLowerCase(Locale.ENGLISH);
String actualOsArch = context.getSystemProperties().get("os.arch").toLowerCase(Locale.ENGLISH);
String actualOsVersion = context.getSystemProperties().get("os.version").toLowerCase(Locale.ENGLISH);
if (active && os.getFamily() != null) {
active = determineFamilyMatch(os.getFamily());
active = determineFamilyMatch(os.getFamily(), actualOsName);
}
if (active && os.getName() != null) {
active = determineNameMatch(os.getName());
active = determineNameMatch(os.getName(), actualOsName);
}
if (active && os.getArch() != null) {
active = determineArchMatch(os.getArch());
active = determineArchMatch(os.getArch(), actualOsArch);
}
if (active && os.getVersion() != null) {
active = determineVersionMatch(os.getVersion());
active = determineVersionMatch(os.getVersion(), actualOsVersion);
}
return active;
@ -90,8 +98,25 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
return os.getArch() != null || os.getFamily() != null || os.getName() != null || os.getVersion() != null;
}
private boolean determineVersionMatch(String version) {
String test = version;
private boolean determineVersionMatch(String expectedVersion, String actualVersion) {
String test = expectedVersion;
boolean reverse = false;
final boolean result;
if (test.startsWith(REGEX_PREFIX)) {
result = actualVersion.matches(test.substring(REGEX_PREFIX.length()));
} else {
if (test.startsWith("!")) {
reverse = true;
test = test.substring(1);
}
result = actualVersion.equals(test);
}
return reverse != result;
}
private boolean determineArchMatch(String expectedArch, String actualArch) {
String test = expectedArch;
boolean reverse = false;
if (test.startsWith("!")) {
@ -99,13 +124,13 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
test = test.substring(1);
}
boolean result = Os.isVersion(test);
boolean result = actualArch.equals(test);
return reverse ? !result : result;
}
private boolean determineArchMatch(String arch) {
String test = arch;
private boolean determineNameMatch(String expectedName, String actualName) {
String test = expectedName;
boolean reverse = false;
if (test.startsWith("!")) {
@ -113,26 +138,12 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
test = test.substring(1);
}
boolean result = Os.isArch(test);
boolean result = actualName.equals(test);
return reverse ? !result : result;
}
private boolean determineNameMatch(String name) {
String test = name;
boolean reverse = false;
if (test.startsWith("!")) {
reverse = true;
test = test.substring(1);
}
boolean result = Os.isName(test);
return reverse ? !result : result;
}
private boolean determineFamilyMatch(String family) {
private boolean determineFamilyMatch(String family, String actualName) {
String test = family;
boolean reverse = false;
@ -141,7 +152,7 @@ public class OperatingSystemProfileActivator implements ProfileActivator {
test = test.substring(1);
}
boolean result = Os.isFamily(test);
boolean result = Os.isFamily(test, actualName);
return reverse ? !result : result;
}

View File

@ -0,0 +1,233 @@
/*
* 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.
*/
package org.apache.maven.utils;
import java.util.Locale;
import java.util.stream.Stream;
/**
* OS support
*/
public class Os {
/**
* The OS Name.
*/
public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
/**
* The OA architecture.
*/
public static final String OS_ARCH = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
/**
* The OS version.
*/
public static final String OS_VERSION = System.getProperty("os.version").toLowerCase(Locale.ENGLISH);
/**
* OS Family
*/
public static final String OS_FAMILY;
/**
* Boolean indicating if the running OS is a Windows system.
*/
public static final boolean IS_WINDOWS;
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_WINDOWS = "windows";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_WIN9X = "win9x";
/**
* OS family that can be tested for. {@value}
*/
public static final String FAMILY_NT = "winnt";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_OS2 = "os/2";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_NETWARE = "netware";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_DOS = "dos";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_MAC = "mac";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_TANDEM = "tandem";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_UNIX = "unix";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_OPENVMS = "openvms";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_ZOS = "z/os";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_OS390 = "os/390";
/**
* OS family that can be tested for. {@value}
*/
private static final String FAMILY_OS400 = "os/400";
/**
* OpenJDK is reported to call MacOS X "Darwin"
*
* @see <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=44889">bugzilla issue</a>
* @see <a href="https://issues.apache.org/jira/browse/HADOOP-3318">HADOOP-3318</a>
*/
private static final String DARWIN = "darwin";
/**
* The path separator.
*/
private static final String PATH_SEP = System.getProperty("path.separator");
static {
// Those two public constants are initialized here, as they need all the private constants
// above to be initialized first, but the code style imposes the public constants to be
// defined above the private ones...
OS_FAMILY = getOsFamily();
IS_WINDOWS = isFamily(FAMILY_WINDOWS);
}
private Os() {}
/**
* Determines if the OS on which Maven is executing matches the
* given OS family.
*
* @param family the family to check for
* @return true if the OS matches
*
*/
public static boolean isFamily(String family) {
return isFamily(family, OS_NAME);
}
/**
* Determines if the OS on which Maven is executing matches the
* given OS family derived from the given OS name
*
* @param family the family to check for
* @param actualOsName the OS name to check against
* @return true if the OS matches
*
*/
public static boolean isFamily(String family, String actualOsName) {
// windows probing logic relies on the word 'windows' in the OS
boolean isWindows = actualOsName.contains(FAMILY_WINDOWS);
boolean is9x = false;
boolean isNT = false;
if (isWindows) {
// there are only four 9x platforms that we look for
is9x = (actualOsName.contains("95")
|| actualOsName.contains("98")
|| actualOsName.contains("me")
// wince isn't really 9x, but crippled enough to
// be a muchness. Maven doesnt run on CE, anyway.
|| actualOsName.contains("ce"));
isNT = !is9x;
}
switch (family) {
case FAMILY_WINDOWS:
return isWindows;
case FAMILY_WIN9X:
return isWindows && is9x;
case FAMILY_NT:
return isWindows && isNT;
case FAMILY_OS2:
return actualOsName.contains(FAMILY_OS2);
case FAMILY_NETWARE:
return actualOsName.contains(FAMILY_NETWARE);
case FAMILY_DOS:
return PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE, actualOsName) && !isWindows;
case FAMILY_MAC:
return actualOsName.contains(FAMILY_MAC) || actualOsName.contains(DARWIN);
case FAMILY_TANDEM:
return actualOsName.contains("nonstop_kernel");
case FAMILY_UNIX:
return PATH_SEP.equals(":")
&& !isFamily(FAMILY_OPENVMS, actualOsName)
&& (!isFamily(FAMILY_MAC, actualOsName) || actualOsName.endsWith("x"));
case FAMILY_ZOS:
return actualOsName.contains(FAMILY_ZOS) || actualOsName.contains(FAMILY_OS390);
case FAMILY_OS400:
return actualOsName.contains(FAMILY_OS400);
case FAMILY_OPENVMS:
return actualOsName.contains(FAMILY_OPENVMS);
default:
return actualOsName.contains(family.toLowerCase(Locale.US));
}
}
/**
* Helper method to determine the current OS family.
*
* @return name of current OS family.
*/
private static String getOsFamily() {
return Stream.of(
FAMILY_DOS,
FAMILY_MAC,
FAMILY_NETWARE,
FAMILY_NT,
FAMILY_OPENVMS,
FAMILY_OS2,
FAMILY_OS400,
FAMILY_TANDEM,
FAMILY_UNIX,
FAMILY_WIN9X,
FAMILY_WINDOWS,
FAMILY_ZOS)
.filter(Os::isFamily)
.findFirst()
.orElse(null);
}
}

View File

@ -0,0 +1,144 @@
/*
* 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.
*/
package org.apache.maven.model.profile.activation;
import java.util.Properties;
import org.apache.maven.model.Activation;
import org.apache.maven.model.ActivationOS;
import org.apache.maven.model.Profile;
/**
* Tests {@link OperatingSystemProfileActivator}.
*
*/
public class OperatingSystemProfileActivatorTest extends AbstractProfileActivatorTest<OperatingSystemProfileActivator> {
public OperatingSystemProfileActivatorTest() throws Exception {
super(OperatingSystemProfileActivator.class);
}
private Profile newProfile(ActivationOS os) {
org.apache.maven.model.Activation a = new Activation();
a.setOs(os);
Profile p = new Profile();
p.setActivation(a);
return p;
}
private Properties newProperties(String osName, String osVersion, String osArch) {
Properties props = new Properties();
props.setProperty("os.name", osName);
props.setProperty("os.version", osVersion);
props.setProperty("os.arch", osArch);
return props;
}
public void testVersionStringComparison() throws Exception {
ActivationOS os = new ActivationOS();
os.setVersion("6.5.0-1014-aws");
Profile profile = newProfile(os);
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64")));
}
public void testVersionRegexMatching() throws Exception {
ActivationOS os = new ActivationOS();
os.setVersion("regex:.*aws");
Profile profile = newProfile(os);
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
assertActivation(false, profile, newContext(null, newProperties("linux", "3.1.0", "amd64")));
}
public void testName() {
ActivationOS os = new ActivationOS();
os.setName("windows");
Profile profile = newProfile(os);
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testNegatedName() {
ActivationOS os = new ActivationOS();
os.setName("!windows");
Profile profile = newProfile(os);
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testArch() {
ActivationOS os = new ActivationOS();
os.setArch("amd64");
Profile profile = newProfile(os);
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testNegatedArch() {
ActivationOS os = new ActivationOS();
os.setArch("!amd64");
Profile profile = newProfile(os);
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testFamily() {
ActivationOS os = new ActivationOS();
os.setFamily("windows");
Profile profile = newProfile(os);
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testNegatedFamily() {
ActivationOS os = new ActivationOS();
os.setFamily("!windows");
Profile profile = newProfile(os);
assertActivation(true, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(false, profile, newContext(null, newProperties("windows", "6.5.0-1014-aws", "aarch64")));
}
public void testAllOsConditions() {
ActivationOS os = new ActivationOS();
os.setFamily("windows");
os.setName("windows");
os.setArch("aarch64");
os.setVersion("99");
Profile profile = newProfile(os);
assertActivation(false, profile, newContext(null, newProperties("linux", "6.5.0-1014-aws", "amd64")));
assertActivation(false, profile, newContext(null, newProperties("windows", "1", "aarch64")));
assertActivation(false, profile, newContext(null, newProperties("windows", "99", "amd64")));
assertActivation(true, profile, newContext(null, newProperties("windows", "99", "aarch64")));
}
}