|
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; |
33 |
| -import java.util.stream.Collectors; |
| 19 | +import java.net.URI; |
34 | 20 |
|
35 | 21 | import javax.inject.Inject;
|
36 | 22 |
|
37 |
| -import org.gradle.api.DefaultTask; |
38 |
| -import org.gradle.api.InvalidUserDataException; |
39 | 23 | import org.gradle.api.Task;
|
40 | 24 | 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; |
46 | 25 |
|
47 | 26 | 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; |
54 | 27 |
|
55 | 28 | /**
|
56 | 29 | * {@link Task} to upgrade the libraries managed by a bom.
|
57 | 30 | *
|
58 | 31 | * @author Andy Wilkinson
|
59 | 32 | * @author Moritz Halbritter
|
60 | 33 | */
|
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 { |
72 | 35 |
|
73 | 36 | @Inject
|
74 | 37 | public UpgradeBom(BomExtension bom) {
|
75 |
| - this.bom = bom; |
| 38 | + super(bom); |
76 | 39 | 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); |
80 | 43 | }
|
81 | 44 | });
|
82 | 45 | }
|
83 | 46 |
|
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(); |
203 | 50 | }
|
204 | 51 |
|
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; |
216 | 55 | }
|
217 | 56 |
|
218 | 57 | }
|
0 commit comments