Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddParentPom recipe #4289

Merged
merged 21 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
abedad1
[WIP] Adding AddParentPom recipe
rcsilva83 Jun 26, 2024
30d63b3
Apply formatter; drop RepeatedTest; fix precondition
timtebeek Jun 26, 2024
ab360c7
Apply suggestions from code review
timtebeek Jun 26, 2024
1204724
Fixing some test expectations on AddParentPomTest
rcsilva83 Jun 27, 2024
ea1e369
Fixing multiple issues on AddParentPom recipe
rcsilva83 Jun 27, 2024
c1cce07
Fixing expectations on AddParentPomTest
rcsilva83 Jun 27, 2024
fb121e8
Fixing expectations on AddParentPomTest
rcsilva83 Jun 27, 2024
5a5634f
Testing FilePattern param on AddParentPom recipe
rcsilva83 Jun 27, 2024
1da518c
Fix wildcard version
rcsilva83 Jun 28, 2024
66d716a
Fixing tests
rcsilva83 Jun 28, 2024
314b0dd
Better error handling
rcsilva83 Jun 28, 2024
4f0e60f
Apply formatter and drop unused code
timtebeek Jun 28, 2024
c8ec4aa
Merge branch 'main' into add-maven-parent
timtebeek Jul 2, 2024
4efbc91
Adding tests with non-existing version
rcsilva83 Jul 3, 2024
a6cb885
Moved logic to `visitDocument` method to fix the empty parent tag whe…
rcsilva83 Jul 3, 2024
296c771
Remove useless test cases
rcsilva83 Jul 3, 2024
91ca361
Removing filePattern option in favor of Preconditions.
rcsilva83 Jul 3, 2024
305e3fb
Fixing description
rcsilva83 Jul 3, 2024
1f13889
Restore `maybeUpdateModel()` before `RemoveRedundantDependencyVersions`
timtebeek Jul 3, 2024
09ce19a
Update name and description; move variable inside visitor
timtebeek Jul 3, 2024
d5d6433
Condense and remove duplicate tests
timtebeek Jul 3, 2024
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
163 changes: 163 additions & 0 deletions rewrite-maven/src/main/java/org/openrewrite/maven/AddParentPom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.maven;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.*;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.marker.SearchResult;
import org.openrewrite.maven.table.MavenMetadataFailures;
import org.openrewrite.maven.tree.MavenMetadata;
import org.openrewrite.maven.tree.MavenRepository;
import org.openrewrite.semver.Semver;
import org.openrewrite.semver.VersionComparator;
import org.openrewrite.xml.AddToTagVisitor;
import org.openrewrite.xml.tree.Xml;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Value
@EqualsAndHashCode(callSuper = false)
public class AddParentPom extends Recipe {
transient MavenMetadataFailures metadataFailures = new MavenMetadataFailures(this);

@Option(displayName = "Group ID",
description = "The group ID of the maven parent pom to be adopted.",
example = "org.springframework.boot")
String groupId;

@Option(displayName = "Artifact ID",
description = "The artifact ID of the maven parent pom to be adopted.",
example = "spring-boot-starter-parent")
String artifactId;

@Option(displayName = "Version",
description = "An exact version number or node-style semver selector used to select the version number.",
example = "29.X")
String version;

@Option(displayName = "Relative path",
description = "New relative path attribute for parent lookup.",
example = "../pom.xml")
@Nullable
String relativePath;

@Option(displayName = "Version pattern",
description = "Allows version selection to be extended beyond the original Node Semver semantics. So for example," +
"Setting 'version' to \"25-29\" can be paired with a metadata pattern of \"-jre\" to select Guava 29.0-jre",
example = "-jre",
required = false)
@Nullable
String versionPattern;

@Override
public String getDisplayName() {
return "Add Maven parent";
}

@Override
public String getInstanceNameSuffix() {
return String.format("`%s:%s:%s`", groupId, artifactId, version);
}

@Override
public String getDescription() {
return "Add a parent pom to a Maven pom.xml. Does nothing if a parent pom is already present.";
}

@Override
public Validated<Object> validate() {
Validated<Object> validated = super.validate();
//noinspection ConstantConditions
if (version != null) {
validated = validated.and(Semver.validate(version, versionPattern));
}
return validated;
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new MavenVisitor<ExecutionContext>() {
@Override
public Xml visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml.Tag root = document.getRoot();
if (!root.getChild("parent").isPresent()) {
return SearchResult.found(document);
}
return document;

}
}, new MavenIsoVisitor<ExecutionContext>() {
@Nullable
private Collection<String> availableVersions;

@Override
public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) {
Xml.Tag root = document.getRoot();
assert !root.getChild("parent").isPresent();

try {
Optional<String> targetVersion = findAcceptableVersion(groupId, artifactId, ctx);
if (targetVersion.isPresent()) {
Xml.Tag parentTag = Xml.Tag.build(
"<parent>\n" +
"<groupId>" + groupId + "</groupId>\n" +
"<artifactId>" + artifactId + "</artifactId>\n" +
"<version>" + targetVersion.get() + "</version>\n" +
(relativePath == null ? "" : StringUtils.isBlank(relativePath) ?
"<relativePath/>" : "<relativePath>" + relativePath + "</relativePath>") +
"</parent>");

document = (Xml.Document) new AddToTagVisitor<>(root, parentTag, new MavenTagInsertionComparator(root.getChildren()))
.visitNonNull(document, ctx, getCursor().getParentOrThrow());

maybeUpdateModel();
doAfterVisit(new RemoveRedundantDependencyVersions(null, null,
RemoveRedundantDependencyVersions.Comparator.GTE, null).getVisitor());
}
} catch (MavenDownloadingException e) {
for (Map.Entry<MavenRepository, String> repositoryResponse : e.getRepositoryResponses().entrySet()) {
MavenRepository repository = repositoryResponse.getKey();
metadataFailures.insertRow(ctx, new MavenMetadataFailures.Row(groupId, artifactId, version,
repository.getUri(), repository.getSnapshots(), repository.getReleases(), repositoryResponse.getValue()));
}
return e.warn(document);
}

return super.visitDocument(document, ctx);
}

private final VersionComparator versionComparator = Objects.requireNonNull(Semver.validate(version, versionPattern).getValue());

private Optional<String> findAcceptableVersion(String groupId, String artifactId, ExecutionContext ctx)
throws MavenDownloadingException {
if (availableVersions == null) {
MavenMetadata mavenMetadata = metadataFailures.insertRows(ctx, () -> downloadMetadata(groupId, artifactId, ctx));
availableVersions = mavenMetadata.getVersioning().getVersions().stream()
.filter(v -> versionComparator.isValid(null, v))
.collect(Collectors.toList());
}
return availableVersions.stream().max(versionComparator);
}
});
}
}
Loading