|
16 | 16 |
|
17 | 17 | package org.springframework.boot.build.bom.bomr;
|
18 | 18 |
|
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; |
| 19 | +import java.net.URI; |
33 | 20 |
|
34 | 21 | import javax.inject.Inject;
|
35 | 22 |
|
36 |
| -import org.gradle.api.DefaultTask; |
37 |
| -import org.gradle.api.InvalidUserDataException; |
38 | 23 | import org.gradle.api.Task;
|
39 | 24 | import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
|
40 |
| -import org.gradle.api.internal.tasks.userinput.UserInputHandler; |
41 |
| -import org.gradle.api.tasks.Input; |
42 |
| -import org.gradle.api.tasks.TaskAction; |
43 |
| -import org.gradle.api.tasks.TaskExecutionException; |
44 |
| -import org.gradle.api.tasks.options.Option; |
45 | 25 |
|
46 | 26 | import org.springframework.boot.build.bom.BomExtension;
|
47 |
| -import org.springframework.boot.build.bom.Library; |
48 |
| -import org.springframework.boot.build.bom.bomr.github.GitHub; |
49 |
| -import org.springframework.boot.build.bom.bomr.github.GitHubRepository; |
50 |
| -import org.springframework.boot.build.bom.bomr.github.Issue; |
51 |
| -import org.springframework.boot.build.bom.bomr.github.Milestone; |
52 |
| -import org.springframework.util.StringUtils; |
53 | 27 |
|
54 | 28 | /**
|
55 | 29 | * {@link Task} to upgrade the libraries managed by a bom.
|
56 | 30 | *
|
57 | 31 | * @author Andy Wilkinson
|
58 | 32 | * @author Moritz Halbritter
|
59 | 33 | */
|
60 |
| -public class UpgradeBom extends DefaultTask { |
61 |
| - |
62 |
| - private final Set<String> repositoryUrls = new LinkedHashSet<>(); |
63 |
| - |
64 |
| - private final BomExtension bom; |
65 |
| - |
66 |
| - private String milestone; |
67 |
| - |
68 |
| - private String libraries; |
69 |
| - |
70 |
| - private int threads = 2; |
| 34 | +public abstract class UpgradeBom extends UpgradeDependencies { |
71 | 35 |
|
72 | 36 | @Inject
|
73 | 37 | public UpgradeBom(BomExtension bom) {
|
74 |
| - this.bom = bom; |
| 38 | + super(bom); |
75 | 39 | getProject().getRepositories().withType(MavenArtifactRepository.class, (repository) -> {
|
76 |
| - String repositoryUrl = repository.getUrl().toString(); |
77 |
| - if (!repositoryUrl.endsWith("snapshot")) { |
78 |
| - this.repositoryUrls.add(repositoryUrl); |
| 40 | + URI repositoryUrl = repository.getUrl(); |
| 41 | + if (!repositoryUrl.toString().endsWith("snapshot")) { |
| 42 | + getRepositoryUris().add(repositoryUrl); |
79 | 43 | }
|
80 | 44 | });
|
81 | 45 | }
|
82 | 46 |
|
83 |
| - @Option(option = "milestone", description = "Milestone to which dependency upgrade issues should be assigned") |
84 |
| - public void setMilestone(String milestone) { |
85 |
| - this.milestone = milestone; |
86 |
| - } |
87 |
| - |
88 |
| - @Option(option = "threads", description = "Number of Threads to use for update resolution") |
89 |
| - public void setThreads(String threads) { |
90 |
| - this.threads = Integer.parseInt(threads); |
91 |
| - } |
92 |
| - |
93 |
| - @Input |
94 |
| - public String getMilestone() { |
95 |
| - return this.milestone; |
96 |
| - } |
97 |
| - |
98 |
| - @Option(option = "libraries", description = "Regular expression that identifies the libraries to upgrade") |
99 |
| - public void setLibraries(String libraries) { |
100 |
| - this.libraries = libraries; |
101 |
| - } |
102 |
| - |
103 |
| - @Input |
104 |
| - @org.gradle.api.tasks.Optional |
105 |
| - public String getLibraries() { |
106 |
| - return this.libraries; |
107 |
| - } |
108 |
| - |
109 |
| - @TaskAction |
110 |
| - @SuppressWarnings("deprecation") |
111 |
| - void upgradeDependencies() { |
112 |
| - GitHubRepository repository = createGitHub().getRepository(this.bom.getUpgrade().getGitHub().getOrganization(), |
113 |
| - this.bom.getUpgrade().getGitHub().getRepository()); |
114 |
| - Set<String> availableLabels = repository.getLabels(); |
115 |
| - List<String> issueLabels = this.bom.getUpgrade().getGitHub().getIssueLabels(); |
116 |
| - if (!availableLabels.containsAll(issueLabels)) { |
117 |
| - List<String> unknownLabels = new ArrayList<>(issueLabels); |
118 |
| - unknownLabels.removeAll(availableLabels); |
119 |
| - throw new InvalidUserDataException( |
120 |
| - "Unknown label(s): " + StringUtils.collectionToCommaDelimitedString(unknownLabels)); |
121 |
| - } |
122 |
| - Milestone milestone = determineMilestone(repository); |
123 |
| - List<Issue> existingUpgradeIssues = repository.findIssues(issueLabels, milestone); |
124 |
| - List<Upgrade> upgrades = new InteractiveUpgradeResolver(getServices().get(UserInputHandler.class), |
125 |
| - new MultithreadedLibraryUpdateResolver(new MavenMetadataVersionResolver(this.repositoryUrls), |
126 |
| - this.bom.getUpgrade().getPolicy(), this.threads)) |
127 |
| - .resolveUpgrades(matchingLibraries(this.libraries), this.bom.getLibraries()); |
128 |
| - Path buildFile = getProject().getBuildFile().toPath(); |
129 |
| - Path gradleProperties = new File(getProject().getRootProject().getProjectDir(), "gradle.properties").toPath(); |
130 |
| - UpgradeApplicator upgradeApplicator = new UpgradeApplicator(buildFile, gradleProperties); |
131 |
| - for (Upgrade upgrade : upgrades) { |
132 |
| - String title = "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion(); |
133 |
| - Issue existingUpgradeIssue = findExistingUpgradeIssue(existingUpgradeIssues, upgrade); |
134 |
| - if (existingUpgradeIssue != null) { |
135 |
| - System.out.println(title + " (supersedes #" + existingUpgradeIssue.getNumber() + " " |
136 |
| - + existingUpgradeIssue.getTitle() + ")"); |
137 |
| - } |
138 |
| - else { |
139 |
| - System.out.println(title); |
140 |
| - } |
141 |
| - try { |
142 |
| - Path modified = upgradeApplicator.apply(upgrade); |
143 |
| - int issueNumber = repository.openIssue(title, |
144 |
| - (existingUpgradeIssue != null) ? "Supersedes #" + existingUpgradeIssue.getNumber() : "", |
145 |
| - issueLabels, milestone); |
146 |
| - if (existingUpgradeIssue != null) { |
147 |
| - existingUpgradeIssue.label(Arrays.asList("type: task", "status: superseded")); |
148 |
| - } |
149 |
| - if (new ProcessBuilder().command("git", "add", modified.toFile().getAbsolutePath()).start() |
150 |
| - .waitFor() != 0) { |
151 |
| - throw new IllegalStateException("git add failed"); |
152 |
| - } |
153 |
| - if (new ProcessBuilder().command("git", "commit", "-m", title + "\n\nCloses gh-" + issueNumber).start() |
154 |
| - .waitFor() != 0) { |
155 |
| - throw new IllegalStateException("git commit failed"); |
156 |
| - } |
157 |
| - } |
158 |
| - catch (IOException ex) { |
159 |
| - throw new TaskExecutionException(this, ex); |
160 |
| - } |
161 |
| - catch (InterruptedException ex) { |
162 |
| - Thread.currentThread().interrupt(); |
163 |
| - } |
164 |
| - } |
165 |
| - } |
166 |
| - |
167 |
| - private List<Library> matchingLibraries(String pattern) { |
168 |
| - if (pattern == null) { |
169 |
| - return this.bom.getLibraries(); |
170 |
| - } |
171 |
| - Predicate<String> libraryPredicate = Pattern.compile(pattern).asPredicate(); |
172 |
| - List<Library> matchingLibraries = this.bom.getLibraries().stream() |
173 |
| - .filter((library) -> libraryPredicate.test(library.getName())).toList(); |
174 |
| - if (matchingLibraries.isEmpty()) { |
175 |
| - throw new InvalidUserDataException("No libraries matched '" + pattern + "'"); |
176 |
| - } |
177 |
| - return matchingLibraries; |
178 |
| - } |
179 |
| - |
180 |
| - private Issue findExistingUpgradeIssue(List<Issue> existingUpgradeIssues, Upgrade upgrade) { |
181 |
| - String toMatch = "Upgrade to " + upgrade.getLibrary().getName(); |
182 |
| - for (Issue existingUpgradeIssue : existingUpgradeIssues) { |
183 |
| - if (existingUpgradeIssue.getTitle().substring(0, existingUpgradeIssue.getTitle().lastIndexOf(' ')) |
184 |
| - .equals(toMatch)) { |
185 |
| - return existingUpgradeIssue; |
186 |
| - } |
187 |
| - } |
188 |
| - return null; |
189 |
| - } |
190 |
| - |
191 |
| - private GitHub createGitHub() { |
192 |
| - Properties bomrProperties = new Properties(); |
193 |
| - try (Reader reader = new FileReader(new File(System.getProperty("user.home"), ".bomr.properties"))) { |
194 |
| - bomrProperties.load(reader); |
195 |
| - String username = bomrProperties.getProperty("bomr.github.username"); |
196 |
| - String password = bomrProperties.getProperty("bomr.github.password"); |
197 |
| - return GitHub.withCredentials(username, password); |
198 |
| - } |
199 |
| - catch (IOException ex) { |
200 |
| - throw new InvalidUserDataException("Failed to load .bomr.properties from user home", ex); |
201 |
| - } |
| 47 | + @Override |
| 48 | + protected String issueTitle(Upgrade upgrade) { |
| 49 | + return "Upgrade to " + upgrade.getLibrary().getName() + " " + upgrade.getVersion(); |
202 | 50 | }
|
203 | 51 |
|
204 |
| - private Milestone determineMilestone(GitHubRepository repository) { |
205 |
| - if (this.milestone == null) { |
206 |
| - return null; |
207 |
| - } |
208 |
| - List<Milestone> milestones = repository.getMilestones(); |
209 |
| - Optional<Milestone> matchingMilestone = milestones.stream() |
210 |
| - .filter((milestone) -> milestone.getName().equals(this.milestone)).findFirst(); |
211 |
| - if (!matchingMilestone.isPresent()) { |
212 |
| - throw new InvalidUserDataException("Unknown milestone: " + this.milestone); |
213 |
| - } |
214 |
| - return matchingMilestone.get(); |
| 52 | + @Override |
| 53 | + protected String commitMessage(Upgrade upgrade, int issueNumber) { |
| 54 | + return issueTitle(upgrade) + "\n\nCloses gh-" + issueNumber; |
215 | 55 | }
|
216 | 56 |
|
217 | 57 | }
|
0 commit comments