Skip to content

Commit 994a7a0

Browse files
committed
Add support to Bomr for moving to snapshots
Closes gh-27002
1 parent bb6dfc1 commit 994a7a0

File tree

7 files changed

+341
-182
lines changed

7 files changed

+341
-182
lines changed

buildSrc/src/main/java/org/springframework/boot/build/bom/BomPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@
3737
import org.springframework.boot.build.MavenRepositoryPlugin;
3838
import org.springframework.boot.build.bom.Library.Group;
3939
import org.springframework.boot.build.bom.Library.Module;
40+
import org.springframework.boot.build.bom.bomr.MoveToSnapshots;
4041
import org.springframework.boot.build.bom.bomr.UpgradeBom;
4142

4243
/**
@@ -63,6 +64,7 @@ public void apply(Project project) {
6364
project);
6465
project.getTasks().create("bomrCheck", CheckBom.class, bom);
6566
project.getTasks().create("bomrUpgrade", UpgradeBom.class, bom);
67+
project.getTasks().create("moveToSnapshots", MoveToSnapshots.class, bom);
6668
new PublishingCustomizer(project, bom).customize();
6769

6870
}

buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/MavenMetadataVersionResolver.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.build.bom.bomr;
1818

1919
import java.io.StringReader;
20+
import java.net.URI;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.HashSet;
@@ -49,29 +50,29 @@ final class MavenMetadataVersionResolver implements VersionResolver {
4950

5051
private final RestTemplate rest;
5152

52-
private final Collection<String> repositoryUrls;
53+
private final Collection<URI> repositoryUrls;
5354

54-
MavenMetadataVersionResolver(Collection<String> repositoryUrls) {
55+
MavenMetadataVersionResolver(Collection<URI> repositoryUrls) {
5556
this(new RestTemplate(Collections.singletonList(new StringHttpMessageConverter())), repositoryUrls);
5657
}
5758

58-
MavenMetadataVersionResolver(RestTemplate restTemplate, Collection<String> repositoryUrls) {
59+
MavenMetadataVersionResolver(RestTemplate restTemplate, Collection<URI> repositoryUrls) {
5960
this.rest = restTemplate;
6061
this.repositoryUrls = repositoryUrls;
6162
}
6263

6364
@Override
6465
public SortedSet<DependencyVersion> resolveVersions(String groupId, String artifactId) {
6566
Set<String> versions = new HashSet<>();
66-
for (String repositoryUrl : this.repositoryUrls) {
67+
for (URI repositoryUrl : this.repositoryUrls) {
6768
versions.addAll(resolveVersions(groupId, artifactId, repositoryUrl));
6869
}
6970
return versions.stream().map(DependencyVersion::parse).collect(Collectors.toCollection(TreeSet::new));
7071
}
7172

72-
private Set<String> resolveVersions(String groupId, String artifactId, String repositoryUrl) {
73+
private Set<String> resolveVersions(String groupId, String artifactId, URI repositoryUrl) {
7374
Set<String> versions = new HashSet<>();
74-
String url = repositoryUrl + "/" + groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml";
75+
URI url = repositoryUrl.resolve(groupId.replace('.', '/') + "/" + artifactId + "/maven-metadata.xml");
7576
try {
7677
String metadata = this.rest.getForObject(url, String.class);
7778
Document metadataDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2021-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.bom.bomr;
18+
19+
import java.net.URI;
20+
21+
import javax.inject.Inject;
22+
23+
import org.gradle.api.Task;
24+
25+
import org.springframework.boot.build.bom.BomExtension;
26+
27+
/**
28+
* A {@link Task} to move to snapshot dependencies.
29+
*
30+
* @author Andy Wilkinson
31+
*/
32+
public abstract class MoveToSnapshots extends UpgradeDependencies {
33+
34+
private final URI REPOSITORY_URI = URI.create("https://repo.spring.io/snapshot/");
35+
36+
@Inject
37+
public MoveToSnapshots(BomExtension bom) {
38+
super(bom);
39+
getRepositoryUris().add(this.REPOSITORY_URI);
40+
}
41+
42+
@Override
43+
protected String issueTitle(Upgrade upgrade) {
44+
String snapshotVersion = upgrade.getVersion().toString();
45+
String releaseVersion = snapshotVersion.substring(0, snapshotVersion.length() - "-SNAPSHOT".length());
46+
return "Upgrade to " + upgrade.getLibrary().getName() + " " + releaseVersion;
47+
}
48+
49+
@Override
50+
protected String commitMessage(Upgrade upgrade, int issueNumber) {
51+
return "Start building against " + upgrade.getLibrary().getName() + " " + releaseVersion(upgrade) + " snapshots"
52+
+ "\n\nSee gh-" + issueNumber;
53+
}
54+
55+
private String releaseVersion(Upgrade upgrade) {
56+
String snapshotVersion = upgrade.getVersion().toString();
57+
return snapshotVersion.substring(0, snapshotVersion.length() - "-SNAPSHOT".length());
58+
}
59+
60+
}

buildSrc/src/main/java/org/springframework/boot/build/bom/bomr/UpgradeBom.java

Lines changed: 12 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -16,203 +16,42 @@
1616

1717
package org.springframework.boot.build.bom.bomr;
1818

19-
import java.io.File;
20-
import java.io.FileReader;
21-
import java.io.IOException;
22-
import java.io.Reader;
23-
import java.nio.file.Path;
24-
import java.util.ArrayList;
25-
import java.util.Arrays;
26-
import java.util.LinkedHashSet;
27-
import java.util.List;
28-
import java.util.Optional;
29-
import java.util.Properties;
30-
import java.util.Set;
31-
import java.util.function.Predicate;
32-
import java.util.regex.Pattern;
33-
import java.util.stream.Collectors;
19+
import java.net.URI;
3420

3521
import javax.inject.Inject;
3622

37-
import org.gradle.api.DefaultTask;
38-
import org.gradle.api.InvalidUserDataException;
3923
import org.gradle.api.Task;
4024
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
41-
import org.gradle.api.internal.tasks.userinput.UserInputHandler;
42-
import org.gradle.api.tasks.Input;
43-
import org.gradle.api.tasks.TaskAction;
44-
import org.gradle.api.tasks.TaskExecutionException;
45-
import org.gradle.api.tasks.options.Option;
4625

4726
import org.springframework.boot.build.bom.BomExtension;
48-
import org.springframework.boot.build.bom.Library;
49-
import org.springframework.boot.build.bom.bomr.github.GitHub;
50-
import org.springframework.boot.build.bom.bomr.github.GitHubRepository;
51-
import org.springframework.boot.build.bom.bomr.github.Issue;
52-
import org.springframework.boot.build.bom.bomr.github.Milestone;
53-
import org.springframework.util.StringUtils;
5427

5528
/**
5629
* {@link Task} to upgrade the libraries managed by a bom.
5730
*
5831
* @author Andy Wilkinson
5932
* @author Moritz Halbritter
6033
*/
61-
public class UpgradeBom extends DefaultTask {
62-
63-
private final Set<String> repositoryUrls = new LinkedHashSet<>();
64-
65-
private final BomExtension bom;
66-
67-
private String milestone;
68-
69-
private String libraries;
70-
71-
private int threads = 2;
34+
public abstract class UpgradeBom extends UpgradeDependencies {
7235

7336
@Inject
7437
public UpgradeBom(BomExtension bom) {
75-
this.bom = bom;
38+
super(bom);
7639
getProject().getRepositories().withType(MavenArtifactRepository.class, (repository) -> {
77-
String repositoryUrl = repository.getUrl().toString();
78-
if (!repositoryUrl.endsWith("snapshot")) {
79-
this.repositoryUrls.add(repositoryUrl);
40+
URI repositoryUrl = repository.getUrl();
41+
if (!repositoryUrl.toString().endsWith("snapshot")) {
42+
getRepositoryUris().add(repositoryUrl);
8043
}
8144
});
8245
}
8346

84-
@Option(option = "milestone", description = "Milestone to which dependency upgrade issues should be assigned")
85-
public void setMilestone(String milestone) {
86-
this.milestone = milestone;
87-
}
88-
89-
@Option(option = "threads", description = "Number of Threads to use for update resolution")
90-
public void setThreads(String threads) {
91-
this.threads = Integer.parseInt(threads);
92-
}
93-
94-
@Input
95-
public String getMilestone() {
96-
return this.milestone;
97-
}
98-
99-
@Option(option = "libraries", description = "Regular expression that identifies the libraries to upgrade")
100-
public void setLibraries(String libraries) {
101-
this.libraries = libraries;
102-
}
103-
104-
@Input
105-
@org.gradle.api.tasks.Optional
106-
public String getLibraries() {
107-
return this.libraries;
108-
}
109-
110-
@TaskAction
111-
@SuppressWarnings("deprecation")
112-
void upgradeDependencies() {
113-
GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(),
114-
this.bom.getUpgrade().getGitHub().getRepository());
115-
Set<String> availableLabels = repository.getLabels();
116-
List<String> issueLabels = this.bom.getUpgrade().getGitHub().getIssueLabels();
117-
if (!availableLabels.containsAll(issueLabels)) {
118-
List<String> unknownLabels = new ArrayList<>(issueLabels);
119-
unknownLabels.removeAll(availableLabels);
120-
throw new InvalidUserDataException(
121-
"Unknown label(s): " + StringUtils.collectionToCommaDelimitedString(unknownLabels));
122-
}
123-
Milestone milestone = determineMilestone(repository);
124-
List<Issue> existingUpgradeIssues = repository.findIssues(issueLabels, milestone);
125-
List<Upgrade> upgrades = new InteractiveUpgradeResolver(getServices().get(UserInputHandler.class),
126-
new MultithreadedLibraryUpdateResolver(new MavenMetadataVersionResolver(this.repositoryUrls),
127-
this.bom.getUpgrade().getPolicy(), this.threads))
128-
.resolveUpgrades(matchingLibraries(this.libraries), this.bom.getLibraries());
129-
Path buildFile = getProject().getBuildFile().toPath();
130-
Path gradleProperties = new File(getProject().getRootProject().getProjectDir(), "gradle.properties").toPath();
131-
UpgradeApplicator upgradeApplicator = new UpgradeApplicator(buildFile, gradleProperties);
132-
for (Upgrade upgrade : upgrades) {
133-
String title = "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion();
134-
Issue existingUpgradeIssue = findExistingUpgradeIssue(existingUpgradeIssues, upgrade);
135-
if (existingUpgradeIssue != null) {
136-
System.out.println(title + " (supersedes #" + existingUpgradeIssue.getNumber() + " "
137-
+ existingUpgradeIssue.getTitle() + ")");
138-
}
139-
else {
140-
System.out.println(title);
141-
}
142-
try {
143-
Path modified = upgradeApplicator.apply(upgrade);
144-
int issueNumber = repository.openIssue(title,
145-
(existingUpgradeIssue != null) ? "Supersedes #" + existingUpgradeIssue.getNumber() : "",
146-
issueLabels, milestone);
147-
if (existingUpgradeIssue != null) {
148-
existingUpgradeIssue.label(Arrays.asList("type: task", "status: superseded"));
149-
}
150-
if (new ProcessBuilder().command("git", "add", modified.toFile().getAbsolutePath()).start()
151-
.waitFor() != 0) {
152-
throw new IllegalStateException("git add failed");
153-
}
154-
if (new ProcessBuilder().command("git", "commit", "-m", title + "\n\nCloses gh-" + issueNumber).start()
155-
.waitFor() != 0) {
156-
throw new IllegalStateException("git commit failed");
157-
}
158-
}
159-
catch (IOException ex) {
160-
throw new TaskExecutionException(this, ex);
161-
}
162-
catch (InterruptedException ex) {
163-
Thread.currentThread().interrupt();
164-
}
165-
}
166-
}
167-
168-
private List<Library> matchingLibraries(String pattern) {
169-
if (pattern == null) {
170-
return this.bom.getLibraries();
171-
}
172-
Predicate<String> libraryPredicate = Pattern.compile(pattern).asPredicate();
173-
List<Library> matchingLibraries = this.bom.getLibraries().stream()
174-
.filter((library) -> libraryPredicate.test(library.getName())).collect(Collectors.toList());
175-
if (matchingLibraries.isEmpty()) {
176-
throw new InvalidUserDataException("No libraries matched '" + pattern + "'");
177-
}
178-
return matchingLibraries;
179-
}
180-
181-
private Issue findExistingUpgradeIssue(List<Issue> existingUpgradeIssues, Upgrade upgrade) {
182-
String toMatch = "Upgrade to " + upgrade.getLibrary().getName();
183-
for (Issue existingUpgradeIssue : existingUpgradeIssues) {
184-
if (existingUpgradeIssue.getTitle().substring(0, existingUpgradeIssue.getTitle().lastIndexOf(' '))
185-
.equals(toMatch)) {
186-
return existingUpgradeIssue;
187-
}
188-
}
189-
return null;
190-
}
191-
192-
private GitHub createGitHub() {
193-
Properties bomrProperties = new Properties();
194-
try (Reader reader = new FileReader(new File(System.getProperty("user.home"), ".bomr.properties"))) {
195-
bomrProperties.load(reader);
196-
String username = bomrProperties.getProperty("bomr.github.username");
197-
String password = bomrProperties.getProperty("bomr.github.password");
198-
return GitHub.withCredentials(username, password);
199-
}
200-
catch (IOException ex) {
201-
throw new InvalidUserDataException("Failed to load .bomr.properties from user home", ex);
202-
}
47+
@Override
48+
protected String issueTitle(Upgrade upgrade) {
49+
return "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion();
20350
}
20451

205-
private Milestone determineMilestone(GitHubRepository repository) {
206-
if (this.milestone == null) {
207-
return null;
208-
}
209-
List<Milestone> milestones = repository.getMilestones();
210-
Optional<Milestone> matchingMilestone = milestones.stream()
211-
.filter((milestone) -> milestone.getName().equals(this.milestone)).findFirst();
212-
if (!matchingMilestone.isPresent()) {
213-
throw new InvalidUserDataException("Unknown milestone: " + this.milestone);
214-
}
215-
return matchingMilestone.get();
52+
@Override
53+
protected String commitMessage(Upgrade upgrade, int issueNumber) {
54+
return issueTitle(upgrade) + "\n\nCloses gh-" + issueNumber;
21655
}
21756

21857
}

0 commit comments

Comments
 (0)