Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,93 @@ String replaceCiFriendlyVersion(Map<String, String> properties, String version)
return version != null ? interpolator.interpolate(version, properties::get) : null;
}

/**
* Get enhanced properties that include profile-aware property resolution.
* This method activates profiles to ensure that properties defined in profiles
* are available for CI-friendly version processing and repository URL interpolation.
* It also includes directory-related properties that may be needed during profile activation.
*/
private Map<String, String> getEnhancedProperties(Model model, Path rootDirectory) {
Map<String, String> properties = new HashMap<>();

// Add directory-specific properties first, as they may be needed for profile activation
if (model.getProjectDirectory() != null) {
String basedir = model.getProjectDirectory().toString();
String basedirUri = model.getProjectDirectory().toUri().toString();
properties.put("basedir", basedir);
properties.put("project.basedir", basedir);
properties.put("project.basedir.uri", basedirUri);
}
try {
String root = rootDirectory.toString();
String rootUri = rootDirectory.toUri().toString();
properties.put("project.rootDirectory", root);
properties.put("project.rootDirectory.uri", rootUri);
} catch (IllegalStateException e) {
// Root directory not available, continue without it
}

// Handle root vs non-root project properties with profile activation
if (!Objects.equals(rootDirectory, model.getProjectDirectory())) {
Path rootModelPath = modelProcessor.locateExistingPom(rootDirectory);
if (rootModelPath != null) {
Model rootModel = derive(Sources.buildSource(rootModelPath)).readFileModel();
properties.putAll(getPropertiesWithProfiles(rootModel, properties));
}
} else {
properties.putAll(getPropertiesWithProfiles(model, properties));
}

return properties;
}

/**
* Get properties from a model including properties from activated profiles.
* This performs lightweight profile activation to merge profile properties.
*
* @param model the model to get properties from
* @param baseProperties base properties (including directory properties) to include in profile activation context
*/
private Map<String, String> getPropertiesWithProfiles(Model model, Map<String, String> baseProperties) {
Map<String, String> properties = new HashMap<>();

// Start with base properties (including directory properties)
properties.putAll(baseProperties);

// Add model properties
properties.putAll(model.getProperties());

try {
// Create a profile activation context for this model with base properties available
DefaultProfileActivationContext profileContext = getProfileActivationContext(request, model);

// Activate profiles and merge their properties
List<Profile> activeProfiles = getActiveProfiles(model.getProfiles(), profileContext);

for (Profile profile : activeProfiles) {
properties.putAll(profile.getProperties());
}
} catch (Exception e) {
// If profile activation fails, log a warning but continue with base properties
// This ensures that CI-friendly versions still work even if profile activation has issues
logger.warn("Failed to activate profiles for CI-friendly version processing: {}", e.getMessage());
logger.debug("Profile activation failure details", e);
}

// User properties override everything
properties.putAll(session.getEffectiveProperties());

return properties;
}

/**
* Convenience method for getting properties with profiles without additional base properties.
* This is a backward compatibility method that provides an empty base properties map.
*/
private Map<String, String> getPropertiesWithProfiles(Model model) {
return getPropertiesWithProfiles(model, new HashMap<>());
}

private void buildBuildPom() throws ModelBuilderException {
// Retrieve and normalize the source path, ensuring it's non-null and in absolute form
Path top = request.getSource().getPath();
Expand Down Expand Up @@ -1394,21 +1481,11 @@ Model doReadFileModel() throws ModelBuilderException {
}
}

// CI friendly version
// All expressions are interpolated using user properties and properties
// defined on the root project.
Map<String, String> properties = new HashMap<>();
if (!Objects.equals(rootDirectory, model.getProjectDirectory())) {
Path rootModelPath = modelProcessor.locateExistingPom(rootDirectory);
if (rootModelPath != null) {
Model rootModel =
derive(Sources.buildSource(rootModelPath)).readFileModel();
properties.putAll(rootModel.getProperties());
}
} else {
properties.putAll(model.getProperties());
}
properties.putAll(session.getEffectiveProperties());
// Enhanced property resolution with profile activation for CI-friendly versions and repository URLs
// This includes directory properties, profile properties, and user properties
Map<String, String> properties = getEnhancedProperties(model, rootDirectory);

// CI friendly version processing with profile-aware properties
model = model.with()
.version(replaceCiFriendlyVersion(properties, model.getVersion()))
.parent(
Expand All @@ -1419,22 +1496,8 @@ Model doReadFileModel() throws ModelBuilderException {
model.getParent().getVersion()))
: null)
.build();
// Interpolate repository URLs
if (model.getProjectDirectory() != null) {
String basedir = model.getProjectDirectory().toString();
String basedirUri = model.getProjectDirectory().toUri().toString();
properties.put("basedir", basedir);
properties.put("project.basedir", basedir);
properties.put("project.basedir.uri", basedirUri);
}
try {
String root = request.getSession().getRootDirectory().toString();
String rootUri =
request.getSession().getRootDirectory().toUri().toString();
properties.put("project.rootDirectory", root);
properties.put("project.rootDirectory.uri", rootUri);
} catch (IllegalStateException e) {
}

// Repository URL interpolation with the same profile-aware properties
UnaryOperator<String> callback = properties::get;
model = model.with()
.repositories(interpolateRepository(model.getRepositories(), callback))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,94 @@ public void testMergeRepositories() throws Exception {
assertEquals("central", repositories.get(3).getId()); // default
}

@Test
public void testCiFriendlyVersionWithProfiles() {
// Test case 1: Default profile should set revision to baseVersion+dev
ModelBuilderRequest request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("ci-friendly-profiles")))
.build();
ModelBuilderResult result = builder.newSession().build(request);
assertNotNull(result);
assertEquals("0.2.0+dev", result.getEffectiveModel().getVersion());

// Test case 2: Release profile should set revision to baseVersion only
request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("ci-friendly-profiles")))
.activeProfileIds(List.of("releaseBuild"))
.build();
result = builder.newSession().build(request);
assertNotNull(result);
assertEquals("0.2.0", result.getEffectiveModel().getVersion());
}

@Test
public void testRepositoryUrlInterpolationWithProfiles() {
// Test case 1: Default properties should be used
ModelBuilderRequest request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("repository-url-profiles")))
.build();
ModelBuilderResult result = builder.newSession().build(request);
assertNotNull(result);
assertEquals(
"http://default.repo.com/repository/maven-public/",
result.getEffectiveModel().getRepositories().get(0).getUrl());

// Test case 2: Development profile should override repository URL
request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("repository-url-profiles")))
.activeProfileIds(List.of("development"))
.build();
result = builder.newSession().build(request);
assertNotNull(result);
assertEquals(
"http://dev.repo.com/repository/maven-public/",
result.getEffectiveModel().getRepositories().get(0).getUrl());

// Test case 3: Production profile should override repository URL
request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("repository-url-profiles")))
.activeProfileIds(List.of("production"))
.build();
result = builder.newSession().build(request);
assertNotNull(result);
assertEquals(
"http://prod.repo.com/repository/maven-public/",
result.getEffectiveModel().getRepositories().get(0).getUrl());
}

@Test
public void testDirectoryPropertiesInProfilesAndRepositories() {
// Test that directory properties (like ${project.basedir}) are available
// during profile activation and repository URL interpolation
ModelBuilderRequest request = ModelBuilderRequest.builder()
.session(session)
.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
.source(Sources.buildSource(getPom("directory-properties-profiles")))
.activeProfileIds(List.of("local-repo"))
.build();
ModelBuilderResult result = builder.newSession().build(request);
assertNotNull(result);

// Verify CI-friendly version was resolved with profile properties
assertEquals("1.0.0-LOCAL", result.getEffectiveModel().getVersion());

// Verify repository URL was interpolated with directory properties from profile
String expectedUrl =
"file://" + getPom("directory-properties-profiles").getParent().toString() + "/local-repo";
assertEquals(
expectedUrl, result.getEffectiveModel().getRepositories().get(0).getUrl());
}

private Path getPom(String name) {
return Paths.get("src/test/resources/poms/factory/" + name + ".xml").toAbsolutePath();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!---
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.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 http://maven.apache.org/xsd/maven-4.1.0.xsd"
root="true">
<modelVersion>4.1.0</modelVersion>

<groupId>org.apache.maven.test</groupId>
<artifactId>ci-friendly-profiles-test</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>

<properties>
<baseVersion>0.2.0</baseVersion>
<revision>${baseVersion}+dev</revision>
</properties>

<profiles>
<profile>
<id>releaseBuild</id>
<properties>
<revision>${baseVersion}</revision>
</properties>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!---
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.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 http://maven.apache.org/xsd/maven-4.1.0.xsd"
root="true">
<modelVersion>4.1.0</modelVersion>

<groupId>org.apache.maven.test</groupId>
<artifactId>directory-properties-profiles-test</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>

<properties>
<baseVersion>1.0.0</baseVersion>
<revision>${baseVersion}-SNAPSHOT</revision>
<repo.url>http://default.repo.com</repo.url>
</properties>

<profiles>
<!-- Profile that uses directory properties in repository URL -->
<profile>
<id>local-repo</id>
<properties>
<revision>${baseVersion}-LOCAL</revision>
<repo.url>file://${project.basedir}/local-repo</repo.url>
</properties>
</profile>
</profiles>

<repositories>
<repository>
<id>test-repo</id>
<url>${repo.url}</url>
</repository>
</repositories>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!---
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.
-->
<project xmlns="http://maven.apache.org/POM/4.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 http://maven.apache.org/xsd/maven-4.1.0.xsd"
root="true">
<modelVersion>4.1.0</modelVersion>

<groupId>org.apache.maven.test</groupId>
<artifactId>repository-url-profiles-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<properties>
<repo.base.url>http://default.repo.com</repo.base.url>
</properties>

<profiles>
<profile>
<id>development</id>
<properties>
<repo.base.url>http://dev.repo.com</repo.base.url>
</properties>
</profile>

<profile>
<id>production</id>
<properties>
<repo.base.url>http://prod.repo.com</repo.base.url>
</properties>
</profile>
</profiles>

<repositories>
<repository>
<id>company-repo</id>
<url>${repo.base.url}/repository/maven-public/</url>
</repository>
</repositories>

</project>
Loading