Skip to content

Commit 4335730

Browse files
committed
Merge branch 'v1'
2 parents c2872db + d132d8b commit 4335730

8 files changed

Lines changed: 126 additions & 23 deletions

File tree

.github/workflows/perform-release.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,10 @@ jobs:
119119
git commit -am "Next development version"
120120
git push
121121
- name: Announce Release on GChat
122-
env:
123-
VERSION: ${{ inputs.project-version }}
124-
ANNOUNCING_ID: ${{ inputs.slack-announcing-id }}
125-
WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_GCHAT_WEBHOOK_URL }}
126-
run: |
127-
curl -X POST "${WEBHOOK_URL}" -H 'Content-Type: application/json' -d "{ \"text\": \"${ANNOUNCING_ID} $(echo '`')${VERSION}$(echo '`') is available now\" }" || true
122+
uses: spring-io/spring-release-actions/[email protected]
123+
with:
124+
version: ${{ inputs.project-version }}
125+
gchat-webhook-url: ${{ secrets.SPRING_RELEASE_GCHAT_WEBHOOK_URL }}
128126
- name: Delete Previous Version
129127
if: ${{ steps.previous-release-milestone.outputs.version != '' }}
130128
env:

api/github/src/main/java/com/github/api/GitHubApi.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public void createRelease(Repository repository, Release release) {
105105
* @param repository The repository owner/name
106106
* @param milestone The milestone containing a title and due date
107107
*/
108-
public void createMilestone(Repository repository, Milestone milestone) {
108+
public Milestone createMilestone(Repository repository, Milestone milestone) {
109109
var uri = "/repos/%s/%s/milestones".formatted(repository.owner(), repository.name());
110110
// @formatter:off
111111
var httpRequest = requestBuilder(uri)
@@ -114,12 +114,13 @@ public void createMilestone(Repository repository, Milestone milestone) {
114114
.build();
115115
// @formatter:on
116116
try {
117-
performRequest(httpRequest, Void.class);
117+
return performRequest(httpRequest, Milestone.class);
118118
}
119119
catch (HttpClientException ex) {
120120
if (ex.getStatusCode() == 422) {
121121
LOGGER.warning("Unable to create milestone %s: response=%s".formatted(milestone.title(),
122122
ex.getResponseBody()));
123+
return null;
123124
}
124125
else {
125126
throw ex;
@@ -182,6 +183,26 @@ public boolean hasOpenIssues(Repository repository, Long milestone) {
182183
return (issues.length > 0);
183184
}
184185

186+
public Issue createReleaseIssue(Repository repository, Milestone milestone) {
187+
var uri = "/repos/%s/%s/issues".formatted(repository.owner(), repository.name());
188+
Map<String, Object> releaseIssue = Map.of("title", "Release " + milestone.title(), "milestone",
189+
milestone.number(), "labels", List.of("in: build", "type: dependency-upgrade"));
190+
var httpRequest = requestBuilder(uri).POST(bodyValue(releaseIssue)).build();
191+
try {
192+
return performRequest(httpRequest, Issue.class);
193+
}
194+
catch (HttpClientException ex) {
195+
if (ex.getStatusCode() == 422) {
196+
LOGGER.warning("Unable to create milestone %s: response=%s".formatted(milestone.title(),
197+
ex.getResponseBody()));
198+
return null;
199+
}
200+
else {
201+
throw ex;
202+
}
203+
}
204+
}
205+
185206
private HttpRequest.Builder requestBuilder(String uri) {
186207
// @formatter:off
187208
HttpRequest.Builder builder = HttpRequest.newBuilder()

api/github/src/test/java/com/github/api/GitHubApiTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,22 @@ public void closeMilestoneWhenValidParametersThenSuccess() throws Exception {
245245
json.assertThat("$.state", is("closed"));
246246
}
247247

248+
@Test
249+
public void createReleaseIssueWhenMilestoneThenAssignedToMilestone() throws Exception {
250+
this.server.enqueue(json("CreateReleaseIssueResponse.json"));
251+
252+
this.githubApi.createReleaseIssue(this.repository, new Milestone("6.1.9", 12L, Instant.now()));
253+
254+
var recordedRequest = this.server.takeRequest();
255+
assertThat(recordedRequest.getMethod()).isEqualTo("POST");
256+
assertThat(recordedRequest.getPath()).isEqualTo("/repos/spring-projects/spring-security/issues");
257+
assertThat(recordedRequest.getHeader("Accept")).isEqualTo("application/json");
258+
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo("Bearer %s".formatted(AUTH_TOKEN));
259+
260+
var json = JsonAssert.with(recordedRequest.getBody().readString(Charset.defaultCharset()));
261+
json.assertThat("$.title", is("Release 6.1.9"));
262+
}
263+
248264
private static MockResponse json(String path) throws IOException {
249265
return new MockResponse().addHeader("Content-Type", "application/json").setBody(string(path));
250266
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"id": 1,
3+
"node_id": "MDU6SXNzdWUx",
4+
"url": "https://api.github.com/repos/octocat/Hello-World/issues/1347",
5+
"repository_url": "https://api.github.com/repos/octocat/Hello-World",
6+
"labels_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/labels{/name}",
7+
"comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments",
8+
"events_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/events",
9+
"html_url": "https://github.com/octocat/Hello-World/issues/1347",
10+
"number": 1347,
11+
"state": "open",
12+
"title": "Release 6.1.9",
13+
"body": "",
14+
"user": {
15+
"login": "octocat",
16+
"id": 1,
17+
"node_id": "MDQ6VXNlcjE=",
18+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
19+
"gravatar_id": "",
20+
"url": "https://api.github.com/users/octocat",
21+
"html_url": "https://github.com/octocat",
22+
"followers_url": "https://api.github.com/users/octocat/followers",
23+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
24+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
25+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
26+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
27+
"organizations_url": "https://api.github.com/users/octocat/orgs",
28+
"repos_url": "https://api.github.com/users/octocat/repos",
29+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
30+
"received_events_url": "https://api.github.com/users/octocat/received_events",
31+
"type": "User",
32+
"site_admin": false
33+
}
34+
}

core/src/main/java/io/spring/release/SpringReleaseTrain.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public LocalDate getNextReleaseDate(LocalDate startDate) {
105105
this.releaseTrainSpec.getWeekOfMonth().getDayOffset());
106106
currentDate = currentDate.plusMonths(1);
107107
}
108-
while (!trainDate.isAfter(startDate) || trainDate.getMonthValue() % 2 != 0);
108+
while (!isEligibleDate(startDate, trainDate));
109109

110110
return trainDate;
111111
}
@@ -128,4 +128,9 @@ private static LocalDate calculateReleaseDate(Year year, Month month, DayOfWeek
128128
return firstMondayOfMonth.with(nextDayOfWeek).plusDays(dayOffset);
129129
}
130130

131+
private static boolean isEligibleDate(LocalDate startDate, LocalDate trainDate) {
132+
return trainDate.isAfter(startDate) && !trainDate.getMonth().equals(startDate.getMonth())
133+
&& trainDate.getMonthValue() % 2 == 0;
134+
}
135+
131136
}

core/src/main/java/io/spring/release/SpringReleases.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,15 +363,17 @@ public void scheduleReleaseIfNotExists(String owner, String repo, String version
363363
// We use 12pm/noon UTC to be as far from anybody's midnight as we can.
364364
var dueOn = releaseDate.atTime(LocalTime.NOON).toInstant(ZoneOffset.UTC);
365365
var milestone = new Milestone(milestoneTitle, null, dueOn);
366-
this.gitHubApi.createMilestone(repository, milestone);
366+
Milestone created = this.gitHubApi.createMilestone(repository, milestone);
367+
this.gitHubApi.createReleaseIssue(repository, created);
367368
});
368369
}
369370
else {
370371
// Create GA milestone for patch release on the next even month
371372
var nextReleaseDate = releaseTrain.getNextReleaseDate(startDate);
372373
var dueOn = nextReleaseDate.atTime(LocalTime.NOON).toInstant(ZoneOffset.UTC);
373374
var milestone = new Milestone(baseVersion, null, dueOn);
374-
this.gitHubApi.createMilestone(repository, milestone);
375+
Milestone created = this.gitHubApi.createMilestone(repository, milestone);
376+
this.gitHubApi.createReleaseIssue(repository, created);
375377
}
376378
}
377379

core/src/test/java/io/spring/release/SpringReleaseTrainTests.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,22 +236,16 @@ public void isTrainDateWhenTrainOneIsThirdMondayOf2022ThenSuccess() {
236236
// @formatter:off
237237
@CsvSource({
238238
"2022-01-01, 2022-02-21",
239-
"2022-02-01, 2022-02-21",
240239
"2022-02-21, 2022-04-18",
241240
"2022-03-01, 2022-04-18",
242-
"2022-04-01, 2022-04-18",
243241
"2022-04-18, 2022-06-20",
244242
"2022-05-01, 2022-06-20",
245-
"2022-06-01, 2022-06-20",
246243
"2022-06-20, 2022-08-15",
247244
"2022-07-01, 2022-08-15",
248-
"2022-08-01, 2022-08-15",
249245
"2022-08-15, 2022-10-17",
250246
"2022-09-01, 2022-10-17",
251-
"2022-10-01, 2022-10-17",
252247
"2022-10-17, 2022-12-19",
253248
"2022-11-01, 2022-12-19",
254-
"2022-12-01, 2022-12-19",
255249
"2022-12-19, 2023-02-20"
256250
})
257251
// @formatter:on
@@ -271,4 +265,31 @@ public void getNextReleaseDateWhenBoundaryConditionsThenSuccess(LocalDate startD
271265
assertThat(releaseTrain.getNextReleaseDate(startDate)).isEqualTo(expectedDate);
272266
}
273267

268+
// https://github.com/spring-io/spring-security-release-tools/issues/84
269+
@ParameterizedTest
270+
// @formatter:off
271+
@CsvSource({
272+
"2022-02-01, 2022-04-18",
273+
"2022-04-01, 2022-06-20",
274+
"2022-06-01, 2022-08-15",
275+
"2022-08-01, 2022-10-17",
276+
"2022-10-01, 2022-12-19",
277+
"2022-12-01, 2023-02-20"
278+
})
279+
public void getNextReleaseDateWhenEarlyReleaseThenDoesNotRescheduleSameRelease(LocalDate startDate, LocalDate expectedDate) {
280+
// @formatter:off
281+
SpringReleaseTrainSpec releaseTrainSpec =
282+
SpringReleaseTrainSpec.builder()
283+
.train(1)
284+
.version("1.0.0")
285+
.weekOfMonth(3)
286+
.dayOfWeek(1)
287+
.year(2022)
288+
.build();
289+
// @formatter:on
290+
291+
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
292+
assertThat(releaseTrain.getNextReleaseDate(startDate)).isEqualTo(expectedDate);
293+
}
294+
274295
}

core/src/test/java/io/spring/release/SpringReleasesTests.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,6 @@ public void scheduleReleaseIfNotExistsWhenMinorVersionThenReleaseTrainCreated()
467467
var milestoneCaptor = forClass(Milestone.class);
468468
verify(this.gitHubApi).getMilestones(repository);
469469
verify(this.gitHubApi, times(5)).createMilestone(eq(repository), milestoneCaptor.capture());
470-
verifyNoMoreInteractions(this.gitHubApi);
471470

472471
var milestonesCreated = milestoneCaptor.getAllValues().stream().map(Milestone::title).toList();
473472
assertThat(milestonesCreated).containsExactly("6.2.0-M1", "6.2.0-M2", "6.2.0-M3", "6.2.0-RC1", "6.2.0");
@@ -482,7 +481,6 @@ public void scheduleReleaseIfNotExistsWhenMinorVersionIsSnapshotThenReleaseTrain
482481
var milestoneCaptor = forClass(Milestone.class);
483482
verify(this.gitHubApi).getMilestones(repository);
484483
verify(this.gitHubApi, times(5)).createMilestone(eq(repository), milestoneCaptor.capture());
485-
verifyNoMoreInteractions(this.gitHubApi);
486484

487485
var milestonesCreated = milestoneCaptor.getAllValues().stream().map(Milestone::title).toList();
488486
assertThat(milestonesCreated).containsExactly("6.2.0-M1", "6.2.0-M2", "6.2.0-M3", "6.2.0-RC1", "6.2.0");
@@ -517,7 +515,6 @@ public void scheduleReleaseIfNotExistsWhenMinorVersionAndGaVersionMissingFromRel
517515
var milestoneCaptor = forClass(Milestone.class);
518516
verify(this.gitHubApi).getMilestones(repository);
519517
verify(this.gitHubApi).createMilestone(eq(repository), milestoneCaptor.capture());
520-
verifyNoMoreInteractions(this.gitHubApi);
521518

522519
var milestone = milestoneCaptor.getValue();
523520
assertThat(milestone.title()).isEqualTo(version);
@@ -544,7 +541,6 @@ public void scheduleReleaseIfNotExistsWhenPatchVersionThenPatchReleaseCreated()
544541
var milestoneCaptor = forClass(Milestone.class);
545542
verify(this.gitHubApi).getMilestones(repository);
546543
verify(this.gitHubApi).createMilestone(eq(repository), milestoneCaptor.capture());
547-
verifyNoMoreInteractions(this.gitHubApi);
548544

549545
var milestone = milestoneCaptor.getValue();
550546
assertThat(milestone.title()).isEqualTo("6.2.1");
@@ -559,7 +555,6 @@ public void scheduleReleaseIfNotExistsWhenPatchVersionIsSnapshotThenPatchRelease
559555
var milestoneCaptor = forClass(Milestone.class);
560556
verify(this.gitHubApi).getMilestones(repository);
561557
verify(this.gitHubApi).createMilestone(eq(repository), milestoneCaptor.capture());
562-
verifyNoMoreInteractions(this.gitHubApi);
563558

564559
var milestone = milestoneCaptor.getValue();
565560
assertThat(milestone.title()).isEqualTo("6.2.1");
@@ -599,7 +594,6 @@ public void scheduleReleaseIfNotExistsWhenCreatedThenDueOnCorrectDate() {
599594
var milestoneCaptor = forClass(Milestone.class);
600595
verify(this.gitHubApi).getMilestones(repository);
601596
verify(this.gitHubApi).createMilestone(eq(repository), milestoneCaptor.capture());
602-
verifyNoMoreInteractions(this.gitHubApi);
603597

604598
var releaseTrainSpec = SpringReleaseTrainSpec.builder()
605599
.nextTrain()
@@ -615,6 +609,18 @@ public void scheduleReleaseIfNotExistsWhenCreatedThenDueOnCorrectDate() {
615609
assertThat(milestone.dueOn()).isEqualTo(dueOn);
616610
}
617611

612+
@Test
613+
public void scheduleReleaseIfNotExistsWhenSchedulesThenIncludesReleaseIssue() {
614+
var version = "6.1.9";
615+
this.springReleases.scheduleReleaseIfNotExists(OWNER, REPO, version, WEEK_OF_MONTH, DAY_OF_WEEK);
616+
617+
var repository = new Repository(OWNER, REPO);
618+
var milestoneCaptor = forClass(Milestone.class);
619+
verify(this.gitHubApi).getMilestones(repository);
620+
verify(this.gitHubApi).createMilestone(eq(repository), milestoneCaptor.capture());
621+
verify(this.gitHubApi).createReleaseIssue(eq(repository), any());
622+
}
623+
618624
private static Instant toInstant(String date) {
619625
return LocalDate.parse(date).atStartOfDay().toInstant(ZoneOffset.UTC);
620626
}

0 commit comments

Comments
 (0)