|
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