Skip to content

Commit

Permalink
[MNG-8230] Rewrite CI friendly versions (#1710)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet authored Oct 1, 2024
1 parent eefe2c7 commit 885a4b3
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 207 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -45,6 +44,8 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -233,6 +234,8 @@ public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilder
}

protected class DefaultModelBuilderSession implements ModelProblemCollector {
private static final Pattern REGEX = Pattern.compile("\\$\\{([^}]+)}");

final Session session;
final ModelBuilderRequest request;
final DefaultModelBuilderResult result;
Expand Down Expand Up @@ -557,58 +560,15 @@ public void mergeRepositories(List<Repository> toAdd, boolean replace) {
}

//
// Transform raw model to build pom
//
Model transformFileToRaw(Model model) {
Model.Builder builder = Model.newBuilder(model);
builder = handleParent(model, builder);
builder = handleReactorDependencies(model, builder);
builder = handleCiFriendlyVersion(model, builder);
return builder.build();
}

//
// Infer parent information
//
Model.Builder handleParent(Model model, Model.Builder builder) {
Parent parent = model.getParent();
if (parent != null) {
String version = parent.getVersion();
String modVersion = replaceCiFriendlyVersion(version);
if (!Objects.equals(version, modVersion)) {
if (builder == null) {
builder = Model.newBuilder(model);
}
builder.parent(parent.withVersion(modVersion));
}
}
return builder;
}

//
// CI friendly versions
//
Model.Builder handleCiFriendlyVersion(Model model, Model.Builder builder) {
String version = model.getVersion();
String modVersion = replaceCiFriendlyVersion(version);
if (!Objects.equals(version, modVersion)) {
if (builder == null) {
builder = Model.newBuilder(model);
}
builder.version(modVersion);
}
return builder;
}

//
// Transform raw model to build pom.
// Infer inner reactor dependencies version
//
Model.Builder handleReactorDependencies(Model model, Model.Builder builder) {
Model transformFileToRaw(Model model) {
List<Dependency> newDeps = new ArrayList<>();
boolean modified = false;
for (Dependency dep : model.getDependencies()) {
Dependency.Builder depBuilder = null;
if (dep.getVersion() == null) {
Dependency.Builder depBuilder = null;
Model depModel = getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId());
if (depModel != null) {
String version = depModel.getVersion();
Expand All @@ -629,30 +589,35 @@ Model.Builder handleReactorDependencies(Model model, Model.Builder builder) {
depBuilder.groupId(depGroupId).location("groupId", groupIdLocation);
}
}
if (depBuilder != null) {
newDeps.add(depBuilder.build());
modified = true;
} else {
newDeps.add(dep);
}
}
if (depBuilder != null) {
newDeps.add(depBuilder.build());
modified = true;
} else {
newDeps.add(dep);
}
}
if (modified) {
if (builder == null) {
builder = Model.newBuilder(model);
}
builder.dependencies(newDeps);
}
return builder;
return modified ? model.withDependencies(newDeps) : model;
}

String replaceCiFriendlyVersion(String version) {
String replaceCiFriendlyVersion(Map<String, String> properties, String version) {
// TODO: we're using a simple regex here, but we should probably use
// a proper interpolation service to do the replacements
// once one is available in maven-api-impl
// https://issues.apache.org/jira/browse/MNG-8262
if (version != null) {
for (String key : Arrays.asList("changelist", "revision", "sha1")) {
String val = request.getUserProperties().get(key);
if (val != null) {
version = version.replace("${" + key + "}", val);
}
Matcher matcher = REGEX.matcher(version);
if (matcher.find()) {
StringBuilder result = new StringBuilder();
do {
// extract the key inside ${}
String key = matcher.group(1);
// get replacement from the map, or use the original ${xy} if not found
String replacement = properties.getOrDefault(key, "\\" + matcher.group(0));
matcher.appendReplacement(result, replacement);
} while (matcher.find());
matcher.appendTail(result); // Append the remaining part of the string
return result.toString();
}
}
return version;
Expand Down Expand Up @@ -733,7 +698,6 @@ Stream<DefaultModelBuilderResult> results(DefaultModelBuilderResult r) {
return Stream.concat(Stream.of(r), r.getChildren().stream().flatMap(this::results));
}

@SuppressWarnings("checkstyle:MethodLength")
private void loadFromRoot(Path root, Path top) {
try (PhasingExecutor executor = createExecutor()) {
DefaultModelBuilderResult r = Objects.equals(top, root) ? result : new DefaultModelBuilderResult();
Expand Down Expand Up @@ -1212,16 +1176,18 @@ Model readFileModel() throws ModelBuilderException {
Model doReadFileModel() throws ModelBuilderException {
ModelSource modelSource = request.getSource();
Model model;
Path rootDirectory;
setSource(modelSource.getLocation());
logger.debug("Reading file model from " + modelSource.getLocation());
try {
boolean strict = request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM;
// TODO: we do cache, but what if strict does not have the same value?
Path rootDirectory;
try {
rootDirectory = request.getSession().getRootDirectory();
} catch (IllegalStateException ignore) {
rootDirectory = modelSource.getPath();
while (rootDirectory != null && !Files.isDirectory(rootDirectory)) {
rootDirectory = rootDirectory.getParent();
}
}
try (InputStream is = modelSource.openStream()) {
model = modelProcessor.read(XmlReaderRequest.builder()
Expand Down Expand Up @@ -1355,6 +1321,29 @@ Model doReadFileModel() throws ModelBuilderException {
add(Severity.FATAL, Version.V41, "Error discovering subprojects", e);
}
}

// 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())) {
Model rootModel = derive(ModelSource.fromPath(modelProcessor.locateExistingPom(rootDirectory)))
.readFileModel();
properties.putAll(rootModel.getProperties());
} else {
properties.putAll(model.getProperties());
}
properties.putAll(session.getUserProperties());
model = model.with()
.version(replaceCiFriendlyVersion(properties, model.getVersion()))
.parent(
model.getParent() != null
? model.getParent()
.withVersion(replaceCiFriendlyVersion(
properties,
model.getParent().getVersion()))
: null)
.build();
}

for (var transformer : transformers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.model.ModelValidator;
import org.apache.maven.api.services.model.ModelVersionProcessor;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;

Expand Down Expand Up @@ -288,12 +287,8 @@ protected ActivationProperty.Builder transformActivationProperty_Value(

private final Set<String> validProfileIds = new HashSet<>();

private final ModelVersionProcessor versionProcessor;

@Inject
public DefaultModelValidator(ModelVersionProcessor versionProcessor) {
this.versionProcessor = versionProcessor;
}
public DefaultModelValidator() {}

@Override
@SuppressWarnings("checkstyle:MethodLength")
Expand Down Expand Up @@ -418,17 +413,42 @@ public void validateFileModel(

Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0);

validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
if (parent == null) {
validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m);
// The file pom may not contain the modelVersion yet, as it may be set later by the
// ModelVersionXMLFilter.
if (m.getModelVersion() != null && !m.getModelVersion().isEmpty()) {
validateModelVersion(problems, m.getModelVersion(), m, VALID_MODEL_VERSIONS);
}

validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m);
validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
boolean isModelVersion41OrMore = !Objects.equals(ModelBuilder.MODEL_VERSION_4_0_0, m.getModelVersion());
if (isModelVersion41OrMore) {
validateStringNoExpression("groupId", problems, Severity.FATAL, Version.V41, m.getGroupId(), m);

validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
validateStringNoExpression("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);

validateVersionNoExpression("version", problems, Severity.FATAL, Version.V41, m.getVersion(), m);

validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m);
if (parent == null) {
validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m);
if (parent != null) {
validateStringNoExpression(
"groupId", problems, Severity.FATAL, Version.V41, parent.getGroupId(), m);
validateStringNoExpression(
"artifactId", problems, Severity.FATAL, Version.V41, parent.getArtifactId(), m);
validateVersionNoExpression(
"version", problems, Severity.FATAL, Version.V41, parent.getVersion(), m);
}
} else {
validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
if (parent == null) {
validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m);
}

validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m);
validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);

validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m);
if (parent == null) {
validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m);
}
}

validate20RawDependencies(
Expand Down Expand Up @@ -1634,19 +1654,14 @@ private boolean validateVersionNoExpression(

Matcher m = EXPRESSION_NAME_PATTERN.matcher(string.trim());
while (m.find()) {
String property = m.group(1);
if (!versionProcessor.isValidProperty(property)) {
addViolation(
problems,
severity,
version,
fieldName,
null,
"contains an expression but should be a constant.",
tracker);

return false;
}
addViolation(
problems,
severity,
version,
fieldName,
null,
"contains an expression but should be a constant.",
tracker);
}

return true;
Expand Down
Loading

0 comments on commit 885a4b3

Please sign in to comment.