39 changed files with 3299 additions and 590 deletions
@ -0,0 +1,83 @@
@@ -0,0 +1,83 @@
|
||||
name: Update Scheduled Release Version |
||||
|
||||
on: |
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main. |
||||
|
||||
env: |
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |
||||
GRADLE_ENTERPRISE_CACHE_USER: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }} |
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} |
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }} |
||||
|
||||
jobs: |
||||
update_scheduled_release_version: |
||||
name: Initiate Release If Scheduled |
||||
if: ${{ github.repository == 'spring-projects/spring-security' }} |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- id: checkout-source |
||||
name: Checkout Source Code |
||||
uses: actions/checkout@v2 |
||||
with: |
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} |
||||
- id: setup-jdk |
||||
name: Set up JDK 11 |
||||
uses: actions/setup-java@v1 |
||||
with: |
||||
java-version: '11' |
||||
- name: Setup gradle user name |
||||
run: | |
||||
mkdir -p ~/.gradle |
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties |
||||
- name: Setup Gradle |
||||
uses: gradle/gradle-build-action@v2 |
||||
env: |
||||
GRADLE_USER_HOME: ~/.gradle |
||||
- id: check-release-due |
||||
name: Check Release Due |
||||
run: | |
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" |
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" |
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" |
||||
./gradlew gitHubCheckNextVersionDueToday |
||||
echo "::set-output name=is_due_today::$(cat build/github/milestones/is-due-today)" |
||||
- id: check-open-issues |
||||
name: Check for open issues |
||||
if: steps.check-release-due.outputs.is_due_today == 'true' |
||||
run: | |
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" |
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" |
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" |
||||
./gradlew gitHubCheckMilestoneHasNoOpenIssues |
||||
echo "::set-output name=is_open_issues::$(cat build/github/milestones/is-open-issues)" |
||||
- id: validate-release-state |
||||
name: Validate State of Release |
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'true' |
||||
run: | |
||||
echo "The release is due today but there are open issues" |
||||
exit 1 |
||||
- id: update-version-and-push |
||||
name: Update version and push |
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'false' |
||||
run: | |
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" |
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" |
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" |
||||
git config user.name 'github-actions[bot]' |
||||
git config user.email 'github-actions[bot]@users.noreply.github.com' |
||||
./gradlew :updateProjectVersion |
||||
./gradlew :spring-security-docs:antoraUpdateVersion |
||||
updatedVersion=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}') |
||||
git commit -am "Release $updatedVersion" |
||||
git tag $updatedVersion |
||||
git push |
||||
git push origin $updatedVersion |
||||
- id: send-slack-notification |
||||
name: Send Slack message |
||||
if: failure() |
||||
uses: Gamesight/slack-workflow-status@v1.0.1 |
||||
with: |
||||
repo_token: ${{ secrets.GITHUB_TOKEN }} |
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} |
||||
channel: '#spring-security-ci' |
||||
name: 'CI Notifier' |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.antora; |
||||
|
||||
public class AntoraVersionUtils { |
||||
|
||||
public static String getDefaultAntoraVersion(String projectVersion) { |
||||
int preReleaseIndex = getSnapshotIndex(projectVersion); |
||||
return isSnapshot(projectVersion) ? projectVersion.substring(0, preReleaseIndex) : projectVersion; |
||||
} |
||||
|
||||
public static String getDefaultAntoraPrerelease(String projectVersion) { |
||||
if (isSnapshot(projectVersion)) { |
||||
int preReleaseIndex = getSnapshotIndex(projectVersion); |
||||
return projectVersion.substring(preReleaseIndex); |
||||
} |
||||
if (isPreRelease(projectVersion)) { |
||||
return Boolean.TRUE.toString(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static String getDefaultAntoraDisplayVersion(String projectVersion) { |
||||
if (!isSnapshot(projectVersion) && isPreRelease(projectVersion)) { |
||||
return getDefaultAntoraVersion(projectVersion); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private static boolean isSnapshot(String projectVersion) { |
||||
return getSnapshotIndex(projectVersion) >= 0; |
||||
} |
||||
|
||||
private static int getSnapshotIndex(String projectVersion) { |
||||
return projectVersion.lastIndexOf("-SNAPSHOT"); |
||||
} |
||||
|
||||
private static boolean isPreRelease(String projectVersion) { |
||||
return projectVersion.lastIndexOf("-") >= 0; |
||||
} |
||||
} |
||||
@ -0,0 +1,138 @@
@@ -0,0 +1,138 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.antora; |
||||
|
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.GradleException; |
||||
import org.gradle.api.Project; |
||||
import org.gradle.api.file.RegularFileProperty; |
||||
import org.gradle.api.provider.Property; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.InputFile; |
||||
import org.gradle.api.tasks.Optional; |
||||
import org.gradle.api.tasks.OutputFile; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
import org.yaml.snakeyaml.DumperOptions; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import org.yaml.snakeyaml.constructor.Constructor; |
||||
import org.yaml.snakeyaml.nodes.NodeTuple; |
||||
import org.yaml.snakeyaml.nodes.Tag; |
||||
import org.yaml.snakeyaml.representer.Representer; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.FileWriter; |
||||
import java.io.IOException; |
||||
|
||||
import org.springframework.gradle.github.milestones.NextVersionYml; |
||||
|
||||
public abstract class UpdateAntoraVersionTask extends DefaultTask { |
||||
|
||||
@TaskAction |
||||
public void update() throws IOException { |
||||
String projectVersion = getProject().getVersion().toString(); |
||||
File antoraYmlFile = getAntoraYmlFile().getAsFile().get(); |
||||
String updatedAntoraVersion = AntoraVersionUtils.getDefaultAntoraVersion(projectVersion); |
||||
String updatedAntoraPrerelease = AntoraVersionUtils.getDefaultAntoraPrerelease(projectVersion); |
||||
String updatedAntoraDisplayVersion = AntoraVersionUtils.getDefaultAntoraDisplayVersion(projectVersion); |
||||
|
||||
Representer representer = new Representer(); |
||||
representer.getPropertyUtils().setSkipMissingProperties(true); |
||||
|
||||
Yaml yaml = new Yaml(new Constructor(AntoraYml.class), representer); |
||||
AntoraYml antoraYml = yaml.load(new FileInputStream(antoraYmlFile)); |
||||
|
||||
System.out.println("Updating the version parameters in " + antoraYmlFile.getName() + " to version: " |
||||
+ updatedAntoraVersion + ", prerelease: " + updatedAntoraPrerelease + ", display_version: " |
||||
+ updatedAntoraDisplayVersion); |
||||
antoraYml.setVersion(updatedAntoraVersion); |
||||
antoraYml.setPrerelease(updatedAntoraPrerelease); |
||||
antoraYml.setDisplay_version(updatedAntoraDisplayVersion); |
||||
|
||||
FileWriter outputWriter = new FileWriter(antoraYmlFile); |
||||
getYaml().dump(antoraYml, outputWriter); |
||||
} |
||||
|
||||
@InputFile |
||||
public abstract RegularFileProperty getAntoraYmlFile(); |
||||
|
||||
public static class AntoraYml { |
||||
|
||||
private String name; |
||||
|
||||
private String version; |
||||
|
||||
private String prerelease; |
||||
|
||||
private String display_version; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getVersion() { |
||||
return version; |
||||
} |
||||
|
||||
public void setVersion(String version) { |
||||
this.version = version; |
||||
} |
||||
|
||||
public String getPrerelease() { |
||||
return prerelease; |
||||
} |
||||
|
||||
public void setPrerelease(String prerelease) { |
||||
this.prerelease = prerelease; |
||||
} |
||||
|
||||
public String getDisplay_version() { |
||||
return display_version; |
||||
} |
||||
|
||||
public void setDisplay_version(String display_version) { |
||||
this.display_version = display_version; |
||||
} |
||||
|
||||
} |
||||
|
||||
private Yaml getYaml() { |
||||
Representer representer = new Representer() { |
||||
@Override |
||||
protected NodeTuple representJavaBeanProperty(Object javaBean, |
||||
org.yaml.snakeyaml.introspector.Property property, Object propertyValue, Tag customTag) { |
||||
// Don't write out null values
|
||||
if (propertyValue == null) { |
||||
return null; |
||||
} |
||||
else { |
||||
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); |
||||
} |
||||
} |
||||
}; |
||||
representer.addClassTag(AntoraYml.class, Tag.MAP); |
||||
DumperOptions ymlOptions = new DumperOptions(); |
||||
ymlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); |
||||
ymlOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); |
||||
return new Yaml(representer, ymlOptions); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.file.RegularFileProperty; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.Optional; |
||||
import org.gradle.api.tasks.OutputFile; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
import org.yaml.snakeyaml.DumperOptions; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import org.yaml.snakeyaml.nodes.Tag; |
||||
import org.yaml.snakeyaml.representer.Representer; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileWriter; |
||||
import java.io.IOException; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
public abstract class GitHubMilestoneNextReleaseTask extends DefaultTask { |
||||
|
||||
@Input |
||||
private RepositoryRef repository = new RepositoryRef(); |
||||
|
||||
@Input |
||||
@Optional |
||||
private String gitHubAccessToken; |
||||
|
||||
private GitHubMilestoneApi milestones = new GitHubMilestoneApi(); |
||||
|
||||
@TaskAction |
||||
public void calculateNextReleaseMilestone() throws IOException { |
||||
String currentVersion = getProject().getVersion().toString(); |
||||
String nextPreRelease = this.milestones.getNextReleaseMilestone(this.repository, currentVersion); |
||||
System.out.println("The next release milestone is: " + nextPreRelease); |
||||
NextVersionYml nextVersionYml = new NextVersionYml(); |
||||
nextVersionYml.setVersion(nextPreRelease); |
||||
File outputFile = getNextReleaseFile().get().getAsFile(); |
||||
FileWriter outputWriter = new FileWriter(outputFile); |
||||
Yaml yaml = getYaml(); |
||||
yaml.dump(nextVersionYml, outputWriter); |
||||
} |
||||
|
||||
@OutputFile |
||||
public abstract RegularFileProperty getNextReleaseFile(); |
||||
|
||||
public RepositoryRef getRepository() { |
||||
return repository; |
||||
} |
||||
|
||||
public void repository(Action<RepositoryRef> repository) { |
||||
repository.execute(this.repository); |
||||
} |
||||
|
||||
public void setRepository(RepositoryRef repository) { |
||||
this.repository = repository; |
||||
} |
||||
|
||||
public String getGitHubAccessToken() { |
||||
return gitHubAccessToken; |
||||
} |
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) { |
||||
this.gitHubAccessToken = gitHubAccessToken; |
||||
this.milestones = new GitHubMilestoneApi(gitHubAccessToken); |
||||
} |
||||
|
||||
private Yaml getYaml() { |
||||
Representer representer = new Representer(); |
||||
representer.addClassTag(NextVersionYml.class, Tag.MAP); |
||||
DumperOptions ymlOptions = new DumperOptions(); |
||||
ymlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); |
||||
return new Yaml(representer, ymlOptions); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.file.RegularFileProperty; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.InputFile; |
||||
import org.gradle.api.tasks.Optional; |
||||
import org.gradle.api.tasks.OutputFile; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
import org.gradle.work.DisableCachingByDefault; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import org.yaml.snakeyaml.constructor.Constructor; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
@DisableCachingByDefault(because = "the due date needs to be checked every time in case it changes") |
||||
public abstract class GitHubMilestoneNextVersionDueTodayTask extends DefaultTask { |
||||
|
||||
@Input |
||||
private RepositoryRef repository = new RepositoryRef(); |
||||
|
||||
@Input |
||||
@Optional |
||||
private String gitHubAccessToken; |
||||
|
||||
@InputFile |
||||
public abstract RegularFileProperty getNextVersionFile(); |
||||
|
||||
@OutputFile |
||||
public abstract RegularFileProperty getIsDueTodayFile(); |
||||
|
||||
private GitHubMilestoneApi milestones = new GitHubMilestoneApi(); |
||||
|
||||
@TaskAction |
||||
public void checkReleaseDueToday() throws IOException { |
||||
File nextVersionFile = getNextVersionFile().getAsFile().get(); |
||||
Yaml yaml = new Yaml(new Constructor(NextVersionYml.class)); |
||||
NextVersionYml nextVersionYml = yaml.load(new FileInputStream(nextVersionFile)); |
||||
String nextVersion = nextVersionYml.getVersion(); |
||||
if (nextVersion == null) { |
||||
throw new IllegalArgumentException( |
||||
"Could not find version property in provided file " + nextVersionFile.getName()); |
||||
} |
||||
boolean milestoneDueToday = this.milestones.isMilestoneDueToday(this.repository, nextVersion); |
||||
Path isDueTodayPath = getIsDueTodayFile().getAsFile().get().toPath(); |
||||
Files.writeString(isDueTodayPath, String.valueOf(milestoneDueToday)); |
||||
if (milestoneDueToday) { |
||||
System.out.println("The milestone with the title " + nextVersion + " in the repository " + this.repository |
||||
+ " is due today"); |
||||
} |
||||
else { |
||||
System.out.println("The milestone with the title " + nextVersion + " in the repository " |
||||
+ this.repository + " is not due yet"); |
||||
} |
||||
|
||||
} |
||||
|
||||
public RepositoryRef getRepository() { |
||||
return repository; |
||||
} |
||||
|
||||
public void repository(Action<RepositoryRef> repository) { |
||||
repository.execute(this.repository); |
||||
} |
||||
|
||||
public void setRepository(RepositoryRef repository) { |
||||
this.repository = repository; |
||||
} |
||||
|
||||
public String getGitHubAccessToken() { |
||||
return gitHubAccessToken; |
||||
} |
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) { |
||||
this.gitHubAccessToken = gitHubAccessToken; |
||||
this.milestones = new GitHubMilestoneApi(gitHubAccessToken); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.io.IOException; |
||||
import java.time.LocalDate; |
||||
|
||||
import com.google.gson.TypeAdapter; |
||||
import com.google.gson.stream.JsonReader; |
||||
import com.google.gson.stream.JsonWriter; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
class LocalDateAdapter extends TypeAdapter<LocalDate> { |
||||
@Override |
||||
public void write(JsonWriter jsonWriter, LocalDate localDate) throws IOException { |
||||
jsonWriter.value(localDate.toString()); |
||||
} |
||||
|
||||
@Override |
||||
public LocalDate read(JsonReader jsonReader) throws IOException { |
||||
return LocalDate.parse(jsonReader.nextString()); |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.io.IOException; |
||||
import java.time.LocalDateTime; |
||||
import java.time.ZoneOffset; |
||||
import java.time.format.DateTimeFormatter; |
||||
|
||||
import com.google.gson.TypeAdapter; |
||||
import com.google.gson.stream.JsonReader; |
||||
import com.google.gson.stream.JsonWriter; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> { |
||||
@Override |
||||
public void write(JsonWriter jsonWriter, LocalDateTime localDateTime) throws IOException { |
||||
jsonWriter.value(localDateTime.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); |
||||
} |
||||
|
||||
@Override |
||||
public LocalDateTime read(JsonReader jsonReader) throws IOException { |
||||
return LocalDateTime.parse(jsonReader.nextString(), DateTimeFormatter.ISO_ZONED_DATE_TIME); |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
public class NextVersionYml { |
||||
private String version; |
||||
|
||||
public String getVersion() { |
||||
return version; |
||||
} |
||||
|
||||
public void setVersion(String version) { |
||||
this.version = version; |
||||
} |
||||
} |
||||
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.time.LocalDate; |
||||
import java.time.LocalTime; |
||||
|
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class ScheduleNextReleaseTask extends DefaultTask { |
||||
@Input |
||||
private RepositoryRef repository = new RepositoryRef(); |
||||
|
||||
@Input |
||||
private String gitHubAccessToken; |
||||
|
||||
@Input |
||||
private String version; |
||||
|
||||
@Input |
||||
private Integer weekOfMonth; |
||||
|
||||
@Input |
||||
private Integer dayOfWeek; |
||||
|
||||
@TaskAction |
||||
public void scheduleNextRelease() { |
||||
GitHubMilestoneApi gitHubMilestoneApi = new GitHubMilestoneApi(this.gitHubAccessToken); |
||||
String nextReleaseMilestone = gitHubMilestoneApi.getNextReleaseMilestone(this.repository, this.version); |
||||
|
||||
// If the next release contains a dash (e.g. 5.6.0-RC1), it is already scheduled
|
||||
if (nextReleaseMilestone.contains("-")) { |
||||
return; |
||||
} |
||||
|
||||
// Check to see if a scheduled GA version already exists
|
||||
boolean hasExistingMilestone = gitHubMilestoneApi.getMilestones(this.repository).stream() |
||||
.anyMatch(milestone -> nextReleaseMilestone.equals(milestone.getTitle())); |
||||
if (hasExistingMilestone) { |
||||
return; |
||||
} |
||||
|
||||
// Next milestone is either a patch version or minor version
|
||||
// Note: Major versions will be handled like minor and get a release
|
||||
// train which can be manually updated to match the desired schedule.
|
||||
if (nextReleaseMilestone.endsWith(".0")) { |
||||
// Create M1, M2, M3, RC1 and GA milestones for release train
|
||||
getReleaseTrain(nextReleaseMilestone).getTrainDates().forEach((milestoneTitle, dueOn) -> { |
||||
Milestone milestone = new Milestone(); |
||||
milestone.setTitle(milestoneTitle); |
||||
// Note: GitHub seems to store full date/time as UTC then displays
|
||||
// as a date (no time) in your timezone, which means the date will
|
||||
// not always be the same date as we intend.
|
||||
// Using 12pm/noon UTC allows GitHub to schedule and display the
|
||||
// correct date.
|
||||
milestone.setDueOn(dueOn.atTime(LocalTime.NOON)); |
||||
gitHubMilestoneApi.createMilestone(this.repository, milestone); |
||||
}); |
||||
} else { |
||||
// Create GA milestone for patch release on the next even month
|
||||
LocalDate startDate = LocalDate.now(); |
||||
LocalDate dueOn = getReleaseTrain(nextReleaseMilestone).getNextReleaseDate(startDate); |
||||
Milestone milestone = new Milestone(); |
||||
milestone.setTitle(nextReleaseMilestone); |
||||
milestone.setDueOn(dueOn.atTime(LocalTime.NOON)); |
||||
gitHubMilestoneApi.createMilestone(this.repository, milestone); |
||||
} |
||||
} |
||||
|
||||
private SpringReleaseTrain getReleaseTrain(String nextReleaseMilestone) { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.nextTrain() |
||||
.version(nextReleaseMilestone) |
||||
.weekOfMonth(this.weekOfMonth) |
||||
.dayOfWeek(this.dayOfWeek) |
||||
.build(); |
||||
|
||||
return new SpringReleaseTrain(releaseTrainSpec); |
||||
} |
||||
|
||||
public RepositoryRef getRepository() { |
||||
return this.repository; |
||||
} |
||||
|
||||
public void repository(Action<RepositoryRef> repository) { |
||||
repository.execute(this.repository); |
||||
} |
||||
|
||||
public void setRepository(RepositoryRef repository) { |
||||
this.repository = repository; |
||||
} |
||||
|
||||
public String getGitHubAccessToken() { |
||||
return this.gitHubAccessToken; |
||||
} |
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) { |
||||
this.gitHubAccessToken = gitHubAccessToken; |
||||
} |
||||
|
||||
public String getVersion() { |
||||
return this.version; |
||||
} |
||||
|
||||
public void setVersion(String version) { |
||||
this.version = version; |
||||
} |
||||
|
||||
public Integer getWeekOfMonth() { |
||||
return weekOfMonth; |
||||
} |
||||
|
||||
public void setWeekOfMonth(Integer weekOfMonth) { |
||||
this.weekOfMonth = weekOfMonth; |
||||
} |
||||
|
||||
public Integer getDayOfWeek() { |
||||
return dayOfWeek; |
||||
} |
||||
|
||||
public void setDayOfWeek(Integer dayOfWeek) { |
||||
this.dayOfWeek = dayOfWeek; |
||||
} |
||||
} |
||||
@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.time.DayOfWeek; |
||||
import java.time.LocalDate; |
||||
import java.time.Month; |
||||
import java.time.Year; |
||||
import java.time.temporal.TemporalAdjuster; |
||||
import java.time.temporal.TemporalAdjusters; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Spring release train generator based on rules contained in a specification. |
||||
* <p> |
||||
* The rules are: |
||||
* <ol> |
||||
* <li>Train 1 (January-May) or 2 (July-November)</li> |
||||
* <li>Version number (e.g. 0.1.2, 1.0.0, etc.)</li> |
||||
* <li>Week of month (1st, 2nd, 3rd, 4th)</li> |
||||
* <li>Day of week (Monday-Friday)</li> |
||||
* <li>Year (e.g. 2020, 2021, etc.)</li> |
||||
* </ol> |
||||
* |
||||
* The release train generated will contain M1, M2, M3, RC1 and GA versions |
||||
* mapped to their respective dates in the train. |
||||
* |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public final class SpringReleaseTrain { |
||||
private final SpringReleaseTrainSpec releaseTrainSpec; |
||||
|
||||
public SpringReleaseTrain(SpringReleaseTrainSpec releaseTrainSpec) { |
||||
this.releaseTrainSpec = releaseTrainSpec; |
||||
} |
||||
|
||||
/** |
||||
* Calculate release train dates based on the release train specification. |
||||
* |
||||
* @return A mapping of release milestones to scheduled release dates |
||||
*/ |
||||
public Map<String, LocalDate> getTrainDates() { |
||||
Map<String, LocalDate> releaseDates = new LinkedHashMap<>(); |
||||
switch (this.releaseTrainSpec.getTrain()) { |
||||
case ONE: |
||||
addTrainDate(releaseDates, "M1", Month.JANUARY); |
||||
addTrainDate(releaseDates, "M2", Month.FEBRUARY); |
||||
addTrainDate(releaseDates, "M3", Month.MARCH); |
||||
addTrainDate(releaseDates, "RC1", Month.APRIL); |
||||
addTrainDate(releaseDates, null, Month.MAY); |
||||
break; |
||||
case TWO: |
||||
addTrainDate(releaseDates, "M1", Month.JULY); |
||||
addTrainDate(releaseDates, "M2", Month.AUGUST); |
||||
addTrainDate(releaseDates, "M3", Month.SEPTEMBER); |
||||
addTrainDate(releaseDates, "RC1", Month.OCTOBER); |
||||
addTrainDate(releaseDates, null, Month.NOVEMBER); |
||||
break; |
||||
} |
||||
|
||||
return releaseDates; |
||||
} |
||||
|
||||
/** |
||||
* Determine if a given date matches the due date of given version. |
||||
* |
||||
* @param version The version number (e.g. 5.6.0-M1, 5.6.0, etc.) |
||||
* @param expectedDate The expected date |
||||
* @return true if the given date matches the due date of the given version, false otherwise |
||||
*/ |
||||
public boolean isTrainDate(String version, LocalDate expectedDate) { |
||||
return expectedDate.isEqual(getTrainDates().get(version)); |
||||
} |
||||
|
||||
/** |
||||
* Calculate the next release date following the given date. |
||||
* <p> |
||||
* The next release date is always on an even month so that a patch release |
||||
* is the month after the GA version of a release train. This method does |
||||
* not consider the year of the release train, only the given start date. |
||||
* |
||||
* @param startDate The start date |
||||
* @return The next release date following the given date |
||||
*/ |
||||
public LocalDate getNextReleaseDate(LocalDate startDate) { |
||||
LocalDate trainDate; |
||||
LocalDate currentDate = startDate; |
||||
do { |
||||
trainDate = calculateReleaseDate( |
||||
Year.of(currentDate.getYear()), |
||||
currentDate.getMonth(), |
||||
this.releaseTrainSpec.getDayOfWeek().getDayOfWeek(), |
||||
this.releaseTrainSpec.getWeekOfMonth().getDayOffset() |
||||
); |
||||
currentDate = currentDate.plusMonths(1); |
||||
} while (!trainDate.isAfter(startDate) || trainDate.getMonthValue() % 2 != 0); |
||||
|
||||
return trainDate; |
||||
} |
||||
|
||||
private void addTrainDate(Map<String, LocalDate> releaseDates, String milestone, Month month) { |
||||
LocalDate releaseDate = calculateReleaseDate( |
||||
this.releaseTrainSpec.getYear(), |
||||
month, |
||||
this.releaseTrainSpec.getDayOfWeek().getDayOfWeek(), |
||||
this.releaseTrainSpec.getWeekOfMonth().getDayOffset() |
||||
); |
||||
String suffix = (milestone == null) ? "" : "-" + milestone; |
||||
releaseDates.put(this.releaseTrainSpec.getVersion() + suffix, releaseDate); |
||||
} |
||||
|
||||
private static LocalDate calculateReleaseDate(Year year, Month month, DayOfWeek dayOfWeek, int dayOffset) { |
||||
TemporalAdjuster nextMonday = TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY); |
||||
TemporalAdjuster nextDayOfWeek = TemporalAdjusters.nextOrSame(dayOfWeek); |
||||
|
||||
LocalDate firstDayOfMonth = year.atMonth(month).atDay(1); |
||||
LocalDate firstMondayOfMonth = firstDayOfMonth.with(nextMonday); |
||||
|
||||
return firstMondayOfMonth.with(nextDayOfWeek).plusDays(dayOffset); |
||||
} |
||||
} |
||||
@ -0,0 +1,205 @@
@@ -0,0 +1,205 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.time.LocalDate; |
||||
import java.time.Month; |
||||
import java.time.Year; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A specification for a release train. |
||||
* |
||||
* @author Steve Riesenberg |
||||
* @see SpringReleaseTrain |
||||
*/ |
||||
public final class SpringReleaseTrainSpec { |
||||
private final Train train; |
||||
private final String version; |
||||
private final WeekOfMonth weekOfMonth; |
||||
private final DayOfWeek dayOfWeek; |
||||
private final Year year; |
||||
|
||||
public SpringReleaseTrainSpec(Train train, String version, WeekOfMonth weekOfMonth, DayOfWeek dayOfWeek, Year year) { |
||||
this.train = train; |
||||
this.version = version; |
||||
this.weekOfMonth = weekOfMonth; |
||||
this.dayOfWeek = dayOfWeek; |
||||
this.year = year; |
||||
} |
||||
|
||||
public Train getTrain() { |
||||
return train; |
||||
} |
||||
|
||||
public String getVersion() { |
||||
return version; |
||||
} |
||||
|
||||
public WeekOfMonth getWeekOfMonth() { |
||||
return weekOfMonth; |
||||
} |
||||
|
||||
public DayOfWeek getDayOfWeek() { |
||||
return dayOfWeek; |
||||
} |
||||
|
||||
public Year getYear() { |
||||
return year; |
||||
} |
||||
|
||||
public static Builder builder() { |
||||
return new Builder(); |
||||
} |
||||
|
||||
public enum WeekOfMonth { |
||||
FIRST(0), SECOND(7), THIRD(14), FOURTH(21); |
||||
|
||||
private final int dayOffset; |
||||
|
||||
WeekOfMonth(int dayOffset) { |
||||
this.dayOffset = dayOffset; |
||||
} |
||||
|
||||
public int getDayOffset() { |
||||
return dayOffset; |
||||
} |
||||
} |
||||
|
||||
public enum DayOfWeek { |
||||
MONDAY(java.time.DayOfWeek.MONDAY), |
||||
TUESDAY(java.time.DayOfWeek.TUESDAY), |
||||
WEDNESDAY(java.time.DayOfWeek.WEDNESDAY), |
||||
THURSDAY(java.time.DayOfWeek.THURSDAY), |
||||
FRIDAY(java.time.DayOfWeek.FRIDAY); |
||||
|
||||
private final java.time.DayOfWeek dayOfWeek; |
||||
|
||||
DayOfWeek(java.time.DayOfWeek dayOfWeek) { |
||||
this.dayOfWeek = dayOfWeek; |
||||
} |
||||
|
||||
public java.time.DayOfWeek getDayOfWeek() { |
||||
return dayOfWeek; |
||||
} |
||||
} |
||||
|
||||
public enum Train { |
||||
ONE, TWO |
||||
} |
||||
|
||||
public static class Builder { |
||||
private Train train; |
||||
private String version; |
||||
private WeekOfMonth weekOfMonth; |
||||
private DayOfWeek dayOfWeek; |
||||
private Year year; |
||||
|
||||
private Builder() { |
||||
} |
||||
|
||||
public Builder train(int train) { |
||||
switch (train) { |
||||
case 1: this.train = Train.ONE; break; |
||||
case 2: this.train = Train.TWO; break; |
||||
default: throw new IllegalArgumentException("Invalid train: " + train); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public Builder train(Train train) { |
||||
this.train = train; |
||||
return this; |
||||
} |
||||
|
||||
public Builder nextTrain() { |
||||
// Search for next train starting with this month
|
||||
return nextTrain(LocalDate.now().withDayOfMonth(1)); |
||||
} |
||||
|
||||
public Builder nextTrain(LocalDate startDate) { |
||||
Train nextTrain = null; |
||||
|
||||
// Search for next train from a given start date
|
||||
LocalDate currentDate = startDate; |
||||
while (nextTrain == null) { |
||||
if (currentDate.getMonth() == Month.JANUARY) { |
||||
nextTrain = Train.ONE; |
||||
} else if (currentDate.getMonth() == Month.JULY) { |
||||
nextTrain = Train.TWO; |
||||
} |
||||
|
||||
currentDate = currentDate.plusMonths(1); |
||||
} |
||||
|
||||
return train(nextTrain).year(currentDate.getYear()); |
||||
} |
||||
|
||||
public Builder version(String version) { |
||||
this.version = version; |
||||
return this; |
||||
} |
||||
|
||||
public Builder weekOfMonth(int weekOfMonth) { |
||||
switch (weekOfMonth) { |
||||
case 1: this.weekOfMonth = WeekOfMonth.FIRST; break; |
||||
case 2: this.weekOfMonth = WeekOfMonth.SECOND; break; |
||||
case 3: this.weekOfMonth = WeekOfMonth.THIRD; break; |
||||
case 4: this.weekOfMonth = WeekOfMonth.FOURTH; break; |
||||
default: throw new IllegalArgumentException("Invalid weekOfMonth: " + weekOfMonth); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public Builder weekOfMonth(WeekOfMonth weekOfMonth) { |
||||
this.weekOfMonth = weekOfMonth; |
||||
return this; |
||||
} |
||||
|
||||
public Builder dayOfWeek(int dayOfWeek) { |
||||
switch (dayOfWeek) { |
||||
case 1: this.dayOfWeek = DayOfWeek.MONDAY; break; |
||||
case 2: this.dayOfWeek = DayOfWeek.TUESDAY; break; |
||||
case 3: this.dayOfWeek = DayOfWeek.WEDNESDAY; break; |
||||
case 4: this.dayOfWeek = DayOfWeek.THURSDAY; break; |
||||
case 5: this.dayOfWeek = DayOfWeek.FRIDAY; break; |
||||
default: throw new IllegalArgumentException("Invalid dayOfWeek: " + dayOfWeek); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public Builder dayOfWeek(DayOfWeek dayOfWeek) { |
||||
this.dayOfWeek = dayOfWeek; |
||||
return this; |
||||
} |
||||
|
||||
public Builder year(int year) { |
||||
this.year = Year.of(year); |
||||
return this; |
||||
} |
||||
|
||||
public SpringReleaseTrainSpec build() { |
||||
Assert.notNull(train, "train cannot be null"); |
||||
Assert.notNull(version, "version cannot be null"); |
||||
Assert.notNull(weekOfMonth, "weekOfMonth cannot be null"); |
||||
Assert.notNull(dayOfWeek, "dayOfWeek cannot be null"); |
||||
Assert.notNull(year, "year cannot be null"); |
||||
return new SpringReleaseTrainSpec(train, version, weekOfMonth, dayOfWeek, year); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.release; |
||||
|
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class DispatchGitHubWorkflowTask extends DefaultTask { |
||||
@Input |
||||
private RepositoryRef repository = new RepositoryRef(); |
||||
|
||||
@Input |
||||
private String gitHubAccessToken; |
||||
|
||||
@Input |
||||
private String branch; |
||||
|
||||
@Input |
||||
private String workflowId; |
||||
|
||||
@TaskAction |
||||
public void dispatchGitHubWorkflow() { |
||||
GitHubActionsApi gitHubActionsApi = new GitHubActionsApi(this.gitHubAccessToken); |
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch(this.branch, null); |
||||
gitHubActionsApi.dispatchWorkflow(this.repository, this.workflowId, workflowDispatch); |
||||
} |
||||
|
||||
public RepositoryRef getRepository() { |
||||
return repository; |
||||
} |
||||
|
||||
public void repository(Action<RepositoryRef> repository) { |
||||
repository.execute(this.repository); |
||||
} |
||||
|
||||
public void setRepository(RepositoryRef repository) { |
||||
this.repository = repository; |
||||
} |
||||
|
||||
public String getGitHubAccessToken() { |
||||
return gitHubAccessToken; |
||||
} |
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) { |
||||
this.gitHubAccessToken = gitHubAccessToken; |
||||
} |
||||
|
||||
public String getBranch() { |
||||
return branch; |
||||
} |
||||
|
||||
public void setBranch(String branch) { |
||||
this.branch = branch; |
||||
} |
||||
|
||||
public String getWorkflowId() { |
||||
return workflowId; |
||||
} |
||||
|
||||
public void setWorkflowId(String workflowId) { |
||||
this.workflowId = workflowId; |
||||
} |
||||
} |
||||
@ -0,0 +1,98 @@
@@ -0,0 +1,98 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.release; |
||||
|
||||
import java.io.IOException; |
||||
|
||||
import com.google.gson.Gson; |
||||
import com.google.gson.GsonBuilder; |
||||
import okhttp3.Interceptor; |
||||
import okhttp3.MediaType; |
||||
import okhttp3.OkHttpClient; |
||||
import okhttp3.Request; |
||||
import okhttp3.RequestBody; |
||||
import okhttp3.Response; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
/** |
||||
* Manage GitHub Actions. |
||||
* |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class GitHubActionsApi { |
||||
private String baseUrl = "https://api.github.com"; |
||||
|
||||
private final OkHttpClient client; |
||||
|
||||
private final Gson gson = new GsonBuilder().create(); |
||||
|
||||
public GitHubActionsApi() { |
||||
this.client = new OkHttpClient.Builder().build(); |
||||
} |
||||
|
||||
public GitHubActionsApi(String gitHubToken) { |
||||
this.client = new OkHttpClient.Builder() |
||||
.addInterceptor(new AuthorizationInterceptor(gitHubToken)) |
||||
.build(); |
||||
} |
||||
|
||||
public void setBaseUrl(String baseUrl) { |
||||
this.baseUrl = baseUrl; |
||||
} |
||||
|
||||
/** |
||||
* Create a workflow dispatch event. |
||||
* |
||||
* @param repository The repository owner/name |
||||
* @param workflowId The ID of the workflow or the name of the workflow file name |
||||
* @param workflowDispatch The workflow dispatch containing a ref (branch) and optional inputs |
||||
*/ |
||||
public void dispatchWorkflow(RepositoryRef repository, String workflowId, WorkflowDispatch workflowDispatch) { |
||||
String url = this.baseUrl + "/repos/" + repository.getOwner() + "/" + repository.getName() + "/actions/workflows/" + workflowId + "/dispatches"; |
||||
String json = this.gson.toJson(workflowDispatch); |
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), json); |
||||
Request request = new Request.Builder().url(url).post(body).build(); |
||||
try { |
||||
Response response = this.client.newCall(request).execute(); |
||||
if (!response.isSuccessful()) { |
||||
throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s. Got response %s", |
||||
workflowId, repository.getOwner(), repository.getName(), response)); |
||||
} |
||||
} catch (IOException ex) { |
||||
throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s", |
||||
workflowId, repository.getOwner(), repository.getName()), ex); |
||||
} |
||||
} |
||||
|
||||
private static class AuthorizationInterceptor implements Interceptor { |
||||
private final String token; |
||||
|
||||
public AuthorizationInterceptor(String token) { |
||||
this.token = token; |
||||
} |
||||
|
||||
@Override |
||||
public Response intercept(Chain chain) throws IOException { |
||||
Request request = chain.request().newBuilder() |
||||
.addHeader("Authorization", "Bearer " + this.token) |
||||
.build(); |
||||
|
||||
return chain.proceed(request); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.release; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class WorkflowDispatch { |
||||
private String ref; |
||||
private Map<String, Object> inputs; |
||||
|
||||
public WorkflowDispatch() { |
||||
} |
||||
|
||||
public WorkflowDispatch(String ref, Map<String, Object> inputs) { |
||||
this.ref = ref; |
||||
this.inputs = inputs; |
||||
} |
||||
|
||||
public String getRef() { |
||||
return ref; |
||||
} |
||||
|
||||
public void setRef(String ref) { |
||||
this.ref = ref; |
||||
} |
||||
|
||||
public Map<String, Object> getInputs() { |
||||
return inputs; |
||||
} |
||||
|
||||
public void setInputs(Map<String, Object> inputs) { |
||||
this.inputs = inputs; |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.PrintStream; |
||||
import java.util.Arrays; |
||||
import java.util.Scanner; |
||||
|
||||
class CommandLineUtils { |
||||
static void runCommand(File dir, String... args) { |
||||
try { |
||||
Process process = new ProcessBuilder() |
||||
.directory(dir) |
||||
.command(args) |
||||
.start(); |
||||
writeLinesTo(process.getInputStream(), System.out); |
||||
writeLinesTo(process.getErrorStream(), System.out); |
||||
if (process.waitFor() != 0) { |
||||
new RuntimeException("Failed to run " + Arrays.toString(args)); |
||||
} |
||||
} catch (IOException | InterruptedException e) { |
||||
throw new RuntimeException("Failed to run " + Arrays.toString(args), e); |
||||
} |
||||
} |
||||
|
||||
private static void writeLinesTo(InputStream input, PrintStream out) { |
||||
Scanner scanner = new Scanner(input); |
||||
while(scanner.hasNextLine()) { |
||||
out.println(scanner.nextLine()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.util.function.Function; |
||||
|
||||
class FileUtils { |
||||
static void replaceFileText(File file, Function<String, String> replaceText) { |
||||
String buildFileText = readString(file); |
||||
String updatedBuildFileText = replaceText.apply(buildFileText); |
||||
writeString(file, updatedBuildFileText); |
||||
} |
||||
|
||||
static String readString(File file) { |
||||
try { |
||||
byte[] bytes = Files.readAllBytes(file.toPath()); |
||||
return new String(bytes); |
||||
} |
||||
catch (IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
private static void writeString(File file, String text) { |
||||
try { |
||||
Files.write(file.toPath(), text.getBytes()); |
||||
} |
||||
catch (IOException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import org.gradle.api.Action; |
||||
import org.gradle.api.Plugin; |
||||
import org.gradle.api.Project; |
||||
|
||||
public class UpdateProjectVersionPlugin implements Plugin<Project> { |
||||
@Override |
||||
public void apply(Project project) { |
||||
project.getTasks().register("updateProjectVersion", UpdateProjectVersionTask.class, new Action<UpdateProjectVersionTask>() { |
||||
@Override |
||||
public void execute(UpdateProjectVersionTask updateProjectVersionTask) { |
||||
updateProjectVersionTask.setGroup("Release"); |
||||
updateProjectVersionTask.setDescription("Updates the project version to the next release in gradle.properties"); |
||||
updateProjectVersionTask.dependsOn("gitHubNextReleaseMilestone"); |
||||
updateProjectVersionTask.getNextVersionFile().fileProvider(project.provider(() -> project.file("next-release.yml"))); |
||||
} |
||||
}); |
||||
project.getTasks().register("updateToSnapshotVersion", UpdateToSnapshotVersionTask.class, new Action<UpdateToSnapshotVersionTask>() { |
||||
@Override |
||||
public void execute(UpdateToSnapshotVersionTask updateToSnapshotVersionTask) { |
||||
updateToSnapshotVersionTask.setGroup("Release"); |
||||
updateToSnapshotVersionTask.setDescription( |
||||
"Updates the project version to the next snapshot in gradle.properties"); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.Project; |
||||
import org.gradle.api.file.RegularFileProperty; |
||||
import org.gradle.api.tasks.Input; |
||||
import org.gradle.api.tasks.InputFile; |
||||
import org.gradle.api.tasks.Optional; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import org.yaml.snakeyaml.constructor.Constructor; |
||||
|
||||
import java.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.FileNotFoundException; |
||||
|
||||
import org.springframework.gradle.github.milestones.NextVersionYml; |
||||
|
||||
public abstract class UpdateProjectVersionTask extends DefaultTask { |
||||
|
||||
@InputFile |
||||
public abstract RegularFileProperty getNextVersionFile(); |
||||
|
||||
@TaskAction |
||||
public void checkReleaseDueToday() throws FileNotFoundException { |
||||
File nextVersionFile = getNextVersionFile().getAsFile().get(); |
||||
Yaml yaml = new Yaml(new Constructor(NextVersionYml.class)); |
||||
NextVersionYml nextVersionYml = yaml.load(new FileInputStream(nextVersionFile)); |
||||
String nextVersion = nextVersionYml.getVersion(); |
||||
if (nextVersion == null) { |
||||
throw new IllegalArgumentException( |
||||
"Could not find version property in provided file " + nextVersionFile.getName()); |
||||
} |
||||
String currentVersion = getProject().getVersion().toString(); |
||||
File gradlePropertiesFile = getProject().getRootProject().file(Project.GRADLE_PROPERTIES); |
||||
if (!gradlePropertiesFile.exists()) { |
||||
return; |
||||
} |
||||
System.out.println("Updating the project version in " + Project.GRADLE_PROPERTIES + " from " + currentVersion |
||||
+ " to " + nextVersion); |
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> { |
||||
gradlePropertiesText = gradlePropertiesText.replace("version=" + currentVersion, "version=" + nextVersion); |
||||
return gradlePropertiesText; |
||||
}); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* Copyright 2019-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.convention.versions; |
||||
|
||||
import org.gradle.api.DefaultTask; |
||||
import org.gradle.api.Project; |
||||
import org.gradle.api.tasks.TaskAction; |
||||
|
||||
import java.io.File; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
public abstract class UpdateToSnapshotVersionTask extends DefaultTask { |
||||
|
||||
private static final String RELEASE_VERSION_PATTERN = "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-M\\d+|-RC\\d+)?$"; |
||||
|
||||
@TaskAction |
||||
public void updateToSnapshotVersion() { |
||||
String currentVersion = getProject().getVersion().toString(); |
||||
File gradlePropertiesFile = getProject().getRootProject().file(Project.GRADLE_PROPERTIES); |
||||
if (!gradlePropertiesFile.exists()) { |
||||
return; |
||||
} |
||||
String nextVersion = calculateNextSnapshotVersion(currentVersion); |
||||
System.out.println("Updating the project version in " + Project.GRADLE_PROPERTIES + " from " + currentVersion |
||||
+ " to " + nextVersion); |
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> { |
||||
gradlePropertiesText = gradlePropertiesText.replace("version=" + currentVersion, "version=" + nextVersion); |
||||
return gradlePropertiesText; |
||||
}); |
||||
} |
||||
|
||||
private String calculateNextSnapshotVersion(String currentVersion) { |
||||
Pattern releaseVersionPattern = Pattern.compile(RELEASE_VERSION_PATTERN); |
||||
Matcher releaseVersion = releaseVersionPattern.matcher(currentVersion); |
||||
|
||||
if (releaseVersion.find()) { |
||||
String majorSegment = releaseVersion.group(1); |
||||
String minorSegment = releaseVersion.group(2); |
||||
String patchSegment = releaseVersion.group(3); |
||||
String modifier = releaseVersion.group(4); |
||||
if (modifier == null) { |
||||
patchSegment = String.valueOf(Integer.parseInt(patchSegment) + 1); |
||||
} |
||||
System.out.println("modifier = " + modifier); |
||||
return String.format("%s.%s.%s-SNAPSHOT", majorSegment, minorSegment, patchSegment); |
||||
} |
||||
else { |
||||
throw new IllegalStateException( |
||||
"Cannot calculate next snapshot version because the current project version does not conform to the expected format"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,389 +0,0 @@
@@ -1,389 +0,0 @@
|
||||
package io.spring.gradle.github.milestones; |
||||
|
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import okhttp3.mockwebserver.MockResponse; |
||||
import okhttp3.mockwebserver.MockWebServer; |
||||
import okhttp3.mockwebserver.RecordedRequest; |
||||
import org.junit.jupiter.api.AfterEach; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
import org.springframework.gradle.github.milestones.GitHubMilestoneApi; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
|
||||
public class GitHubMilestoneApiTests { |
||||
private GitHubMilestoneApi github; |
||||
|
||||
private RepositoryRef repositoryRef = RepositoryRef.owner("spring-projects").repository("spring-security").build(); |
||||
|
||||
private MockWebServer server; |
||||
|
||||
private String baseUrl; |
||||
|
||||
@BeforeEach |
||||
public void setup() throws Exception { |
||||
this.server = new MockWebServer(); |
||||
this.server.start(); |
||||
this.github = new GitHubMilestoneApi("mock-oauth-token"); |
||||
this.baseUrl = this.server.url("/api").toString(); |
||||
this.github.setBaseUrl(this.baseUrl); |
||||
} |
||||
|
||||
@AfterEach |
||||
public void cleanup() throws Exception { |
||||
this.server.shutdown(); |
||||
} |
||||
|
||||
@Test |
||||
public void findMilestoneNumberByTitleWhenFoundThenSuccess() throws Exception { |
||||
String responseJson = "[\n" + |
||||
" {\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" + |
||||
" \"id\":6611880,\n" + |
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" + |
||||
" \"number\":207,\n" + |
||||
" \"title\":\"5.6.x\",\n" + |
||||
" \"description\":\"\",\n" + |
||||
" \"creator\":{\n" + |
||||
" \"login\":\"jgrandja\",\n" + |
||||
" \"id\":10884212,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" + |
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"open_issues\":1,\n" + |
||||
" \"closed_issues\":0,\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" + |
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" + |
||||
" \"due_on\":null,\n" + |
||||
" \"closed_at\":null\n" + |
||||
" },\n" + |
||||
" {\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" + |
||||
" \"id\":5884208,\n" + |
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" + |
||||
" \"number\":191,\n" + |
||||
" \"title\":\"5.5.0-RC1\",\n" + |
||||
" \"description\":\"\",\n" + |
||||
" \"creator\":{\n" + |
||||
" \"login\":\"jzheaux\",\n" + |
||||
" \"id\":3627351,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" + |
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"open_issues\":21,\n" + |
||||
" \"closed_issues\":23,\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" + |
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" + |
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" + |
||||
" \"closed_at\":null\n" + |
||||
" }\n" + |
||||
"]"; |
||||
this.server.enqueue(new MockResponse().setBody(responseJson)); |
||||
|
||||
long milestoneNumberByTitle = this.github.findMilestoneNumberByTitle(this.repositoryRef, "5.5.0-RC1"); |
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS); |
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get"); |
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100"); |
||||
|
||||
assertThat(milestoneNumberByTitle).isEqualTo(191); |
||||
} |
||||
|
||||
@Test |
||||
public void findMilestoneNumberByTitleWhenNotFoundThenException() throws Exception { |
||||
String responseJson = "[\n" + |
||||
" {\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" + |
||||
" \"id\":6611880,\n" + |
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" + |
||||
" \"number\":207,\n" + |
||||
" \"title\":\"5.6.x\",\n" + |
||||
" \"description\":\"\",\n" + |
||||
" \"creator\":{\n" + |
||||
" \"login\":\"jgrandja\",\n" + |
||||
" \"id\":10884212,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" + |
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"open_issues\":1,\n" + |
||||
" \"closed_issues\":0,\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" + |
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" + |
||||
" \"due_on\":null,\n" + |
||||
" \"closed_at\":null\n" + |
||||
" },\n" + |
||||
" {\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" + |
||||
" \"id\":5884208,\n" + |
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" + |
||||
" \"number\":191,\n" + |
||||
" \"title\":\"5.5.0-RC1\",\n" + |
||||
" \"description\":\"\",\n" + |
||||
" \"creator\":{\n" + |
||||
" \"login\":\"jzheaux\",\n" + |
||||
" \"id\":3627351,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" + |
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"open_issues\":21,\n" + |
||||
" \"closed_issues\":23,\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" + |
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" + |
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" + |
||||
" \"closed_at\":null\n" + |
||||
" }\n" + |
||||
"]"; |
||||
this.server.enqueue(new MockResponse().setBody(responseJson)); |
||||
|
||||
assertThatExceptionOfType(RuntimeException.class) |
||||
.isThrownBy(() -> this.github.findMilestoneNumberByTitle(this.repositoryRef, "missing")); |
||||
} |
||||
|
||||
@Test |
||||
public void isOpenIssuesForMilestoneNumberWhenAllClosedThenFalse() throws Exception { |
||||
String responseJson = "[]"; |
||||
long milestoneNumber = 202; |
||||
this.server.enqueue(new MockResponse().setBody(responseJson)); |
||||
|
||||
assertThat(this.github.isOpenIssuesForMilestoneNumber(this.repositoryRef, milestoneNumber)).isFalse(); |
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS); |
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get"); |
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/issues?per_page=1&milestone=" + milestoneNumber); |
||||
} |
||||
|
||||
@Test |
||||
public void isOpenIssuesForMilestoneNumberWhenOpenIssuesThenTrue() throws Exception { |
||||
String responseJson = "[\n" + |
||||
" {\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562\",\n" + |
||||
" \"repository_url\":\"https://api.github.com/repos/spring-projects/spring-security\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/labels{/name}\",\n" + |
||||
" \"comments_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/comments\",\n" + |
||||
" \"events_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/events\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/pull/9562\",\n" + |
||||
" \"id\":851886504,\n" + |
||||
" \"node_id\":\"MDExOlB1bGxSZXF1ZXN0NjEwMjMzMDcw\",\n" + |
||||
" \"number\":9562,\n" + |
||||
" \"title\":\"Add package-list\",\n" + |
||||
" \"user\":{\n" + |
||||
" \"login\":\"jzheaux\",\n" + |
||||
" \"id\":3627351,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" + |
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"labels\":[\n" + |
||||
" {\n" + |
||||
" \"id\":322225043,\n" + |
||||
" \"node_id\":\"MDU6TGFiZWwzMjIyMjUwNDM=\",\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/labels/in:%20build\",\n" + |
||||
" \"name\":\"in: build\",\n" + |
||||
" \"color\":\"e8f9de\",\n" + |
||||
" \"default\":false,\n" + |
||||
" \"description\":\"An issue in the build\"\n" + |
||||
" },\n" + |
||||
" {\n" + |
||||
" \"id\":322225079,\n" + |
||||
" \"node_id\":\"MDU6TGFiZWwzMjIyMjUwNzk=\",\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/labels/type:%20bug\",\n" + |
||||
" \"name\":\"type: bug\",\n" + |
||||
" \"color\":\"e3d9fc\",\n" + |
||||
" \"default\":false,\n" + |
||||
" \"description\":\"A general bug\"\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"locked\":false,\n" + |
||||
" \"assignee\":{\n" + |
||||
" \"login\":\"rwinch\",\n" + |
||||
" \"id\":362503,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjUwMw==\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/362503?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/rwinch\",\n" + |
||||
" \"html_url\":\"https://github.com/rwinch\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/rwinch/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/rwinch/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/rwinch/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/rwinch/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/rwinch/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/rwinch/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/rwinch/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/rwinch/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/rwinch/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"assignees\":[\n" + |
||||
" {\n" + |
||||
" \"login\":\"rwinch\",\n" + |
||||
" \"id\":362503,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjUwMw==\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/362503?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/rwinch\",\n" + |
||||
" \"html_url\":\"https://github.com/rwinch\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/rwinch/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/rwinch/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/rwinch/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/rwinch/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/rwinch/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/rwinch/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/rwinch/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/rwinch/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/rwinch/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" }\n" + |
||||
" ],\n" + |
||||
" \"milestone\":{\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" + |
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" + |
||||
" \"id\":5884208,\n" + |
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" + |
||||
" \"number\":191,\n" + |
||||
" \"title\":\"5.5.0-RC1\",\n" + |
||||
" \"description\":\"\",\n" + |
||||
" \"creator\":{\n" + |
||||
" \"login\":\"jzheaux\",\n" + |
||||
" \"id\":3627351,\n" + |
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" + |
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" + |
||||
" \"gravatar_id\":\"\",\n" + |
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" + |
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" + |
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" + |
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" + |
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" + |
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" + |
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" + |
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" + |
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" + |
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" + |
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" + |
||||
" \"type\":\"User\",\n" + |
||||
" \"site_admin\":false\n" + |
||||
" },\n" + |
||||
" \"open_issues\":21,\n" + |
||||
" \"closed_issues\":23,\n" + |
||||
" \"state\":\"open\",\n" + |
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" + |
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" + |
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" + |
||||
" \"closed_at\":null\n" + |
||||
" },\n" + |
||||
" \"comments\":0,\n" + |
||||
" \"created_at\":\"2021-04-06T23:47:10Z\",\n" + |
||||
" \"updated_at\":\"2021-04-07T17:00:00Z\",\n" + |
||||
" \"closed_at\":null,\n" + |
||||
" \"author_association\":\"MEMBER\",\n" + |
||||
" \"active_lock_reason\":null,\n" + |
||||
" \"pull_request\":{\n" + |
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/pulls/9562\",\n" + |
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/pull/9562\",\n" + |
||||
" \"diff_url\":\"https://github.com/spring-projects/spring-security/pull/9562.diff\",\n" + |
||||
" \"patch_url\":\"https://github.com/spring-projects/spring-security/pull/9562.patch\"\n" + |
||||
" },\n" + |
||||
" \"body\":\"Closes gh-9528\\r\\n\\r\\n<!--\\r\\nFor Security Vulnerabilities, please use https://pivotal.io/security#reporting\\r\\n-->\\r\\n\\r\\n<!--\\r\\nBefore creating new features, we recommend creating an issue to discuss the feature. This ensures that everyone is on the same page before extensive work is done.\\r\\n\\r\\nThanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with gh-).\\r\\n-->\\r\\n\",\n" + |
||||
" \"performed_via_github_app\":null\n" + |
||||
" }\n" + |
||||
"]"; |
||||
long milestoneNumber = 191; |
||||
this.server.enqueue(new MockResponse().setBody(responseJson)); |
||||
|
||||
assertThat(this.github.isOpenIssuesForMilestoneNumber(this.repositoryRef, milestoneNumber)).isTrue(); |
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS); |
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get"); |
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/issues?per_page=1&milestone=" + milestoneNumber); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,245 @@
@@ -0,0 +1,245 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.milestones; |
||||
|
||||
import java.time.LocalDate; |
||||
import java.time.Year; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.junit.jupiter.params.ParameterizedTest; |
||||
import org.junit.jupiter.params.provider.CsvSource; |
||||
|
||||
import org.springframework.gradle.github.milestones.SpringReleaseTrainSpec.Train; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class SpringReleaseTrainTests { |
||||
@ParameterizedTest |
||||
@CsvSource({ |
||||
"2019-12-31, ONE, 2020", |
||||
"2020-01-01, ONE, 2020", |
||||
"2020-01-31, ONE, 2020", |
||||
"2020-02-01, TWO, 2020", |
||||
"2020-07-31, TWO, 2020", |
||||
"2020-08-01, ONE, 2021" |
||||
}) |
||||
public void nextTrainWhenBoundaryConditionsThenSuccess(LocalDate startDate, Train expectedTrain, Year expectedYear) { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.nextTrain(startDate) |
||||
.version("1.0.0") |
||||
.weekOfMonth(2) |
||||
.dayOfWeek(2) |
||||
.build(); |
||||
assertThat(releaseTrainSpec.getTrain()).isEqualTo(expectedTrain); |
||||
assertThat(releaseTrainSpec.getYear()).isEqualTo(expectedYear); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainOneIsSecondTuesdayOf2020ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(1) |
||||
.version("1.0.0") |
||||
.weekOfMonth(2) |
||||
.dayOfWeek(2) |
||||
.year(2020) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2020, 1, 14)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2020, 2, 11)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2020, 3, 10)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2020, 4, 14)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2020, 5, 12)); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainTwoIsSecondTuesdayOf2020ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(2) |
||||
.version("1.0.0") |
||||
.weekOfMonth(2) |
||||
.dayOfWeek(2) |
||||
.year(2020) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2020, 7, 14)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2020, 8, 11)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2020, 9, 15)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2020, 10, 13)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2020, 11, 10)); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainOneIsSecondTuesdayOf2022ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(1) |
||||
.version("1.0.0") |
||||
.weekOfMonth(2) |
||||
.dayOfWeek(2) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 1, 11)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 2, 15)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 3, 15)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 4, 12)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 5, 10)); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainTwoIsSecondTuesdayOf2022ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(2) |
||||
.version("1.0.0") |
||||
.weekOfMonth(2) |
||||
.dayOfWeek(2) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 7, 12)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 8, 9)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 9, 13)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 10, 11)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 11, 15)); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainOneIsThirdMondayOf2022ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(1) |
||||
.version("1.0.0") |
||||
.weekOfMonth(3) |
||||
.dayOfWeek(1) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 1, 17)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 2, 21)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 3, 21)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 4, 18)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 5, 16)); |
||||
} |
||||
|
||||
@Test |
||||
public void getTrainDatesWhenTrainTwoIsThirdMondayOf2022ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(2) |
||||
.version("1.0.0") |
||||
.weekOfMonth(3) |
||||
.dayOfWeek(1) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates(); |
||||
assertThat(trainDates).hasSize(5); |
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 7, 18)); |
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 8, 15)); |
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 9, 19)); |
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 10, 17)); |
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 11, 21)); |
||||
} |
||||
|
||||
@Test |
||||
public void isTrainDateWhenTrainOneIsThirdMondayOf2022ThenSuccess() { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(1) |
||||
.version("1.0.0") |
||||
.weekOfMonth(3) |
||||
.dayOfWeek(1) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) { |
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M1", LocalDate.of(2022, 1, dayOfMonth))).isEqualTo(dayOfMonth == 17); |
||||
} |
||||
for (int dayOfMonth = 1; dayOfMonth <= 28; dayOfMonth++) { |
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M2", LocalDate.of(2022, 2, dayOfMonth))).isEqualTo(dayOfMonth == 21); |
||||
} |
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) { |
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M3", LocalDate.of(2022, 3, dayOfMonth))).isEqualTo(dayOfMonth == 21); |
||||
} |
||||
for (int dayOfMonth = 1; dayOfMonth <= 30; dayOfMonth++) { |
||||
assertThat(releaseTrain.isTrainDate("1.0.0-RC1", LocalDate.of(2022, 4, dayOfMonth))).isEqualTo(dayOfMonth == 18); |
||||
} |
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) { |
||||
assertThat(releaseTrain.isTrainDate("1.0.0", LocalDate.of(2022, 5, dayOfMonth))).isEqualTo(dayOfMonth == 16); |
||||
} |
||||
} |
||||
|
||||
@ParameterizedTest |
||||
@CsvSource({ |
||||
"2022-01-01, 2022-02-21", |
||||
"2022-02-01, 2022-02-21", |
||||
"2022-02-21, 2022-04-18", |
||||
"2022-03-01, 2022-04-18", |
||||
"2022-04-01, 2022-04-18", |
||||
"2022-04-18, 2022-06-20", |
||||
"2022-05-01, 2022-06-20", |
||||
"2022-06-01, 2022-06-20", |
||||
"2022-06-20, 2022-08-15", |
||||
"2022-07-01, 2022-08-15", |
||||
"2022-08-01, 2022-08-15", |
||||
"2022-08-15, 2022-10-17", |
||||
"2022-09-01, 2022-10-17", |
||||
"2022-10-01, 2022-10-17", |
||||
"2022-10-17, 2022-12-19", |
||||
"2022-11-01, 2022-12-19", |
||||
"2022-12-01, 2022-12-19", |
||||
"2022-12-19, 2023-02-20" |
||||
}) |
||||
public void getNextReleaseDateWhenBoundaryConditionsThenSuccess(LocalDate startDate, LocalDate expectedDate) { |
||||
SpringReleaseTrainSpec releaseTrainSpec = |
||||
SpringReleaseTrainSpec.builder() |
||||
.train(1) |
||||
.version("1.0.0") |
||||
.weekOfMonth(3) |
||||
.dayOfWeek(1) |
||||
.year(2022) |
||||
.build(); |
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec); |
||||
assertThat(releaseTrain.getNextReleaseDate(startDate)).isEqualTo(expectedDate); |
||||
} |
||||
} |
||||
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
/* |
||||
* Copyright 2002-2022 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.gradle.github.release; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import okhttp3.mockwebserver.MockResponse; |
||||
import okhttp3.mockwebserver.MockWebServer; |
||||
import okhttp3.mockwebserver.RecordedRequest; |
||||
import org.junit.jupiter.api.AfterEach; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.gradle.github.RepositoryRef; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* @author Steve Riesenberg |
||||
*/ |
||||
public class GitHubActionsApiTests { |
||||
private GitHubActionsApi gitHubActionsApi; |
||||
|
||||
private MockWebServer server; |
||||
|
||||
private String baseUrl; |
||||
|
||||
private RepositoryRef repository; |
||||
|
||||
@BeforeEach |
||||
public void setup() throws Exception { |
||||
this.server = new MockWebServer(); |
||||
this.server.start(); |
||||
this.baseUrl = this.server.url("/api").toString(); |
||||
this.gitHubActionsApi = new GitHubActionsApi("mock-oauth-token"); |
||||
this.gitHubActionsApi.setBaseUrl(this.baseUrl); |
||||
this.repository = new RepositoryRef("spring-projects", "spring-security"); |
||||
} |
||||
|
||||
@AfterEach |
||||
public void cleanup() throws Exception { |
||||
this.server.shutdown(); |
||||
} |
||||
|
||||
@Test |
||||
public void dispatchWorkflowWhenValidParametersThenSuccess() throws Exception { |
||||
this.server.enqueue(new MockResponse().setResponseCode(204)); |
||||
|
||||
Map<String, Object> inputs = new LinkedHashMap<>(); |
||||
inputs.put("input-1", "value"); |
||||
inputs.put("input-2", false); |
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", inputs); |
||||
this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch); |
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS); |
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post"); |
||||
assertThat(recordedRequest.getRequestUrl().toString()) |
||||
.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/actions/workflows/test-workflow.yml/dispatches"); |
||||
assertThat(recordedRequest.getBody().readString(Charset.defaultCharset())) |
||||
.isEqualTo("{\"ref\":\"main\",\"inputs\":{\"input-1\":\"value\",\"input-2\":false}}"); |
||||
} |
||||
|
||||
@Test |
||||
public void dispatchWorkflowWhenErrorResponseThenException() throws Exception { |
||||
this.server.enqueue(new MockResponse().setResponseCode(400)); |
||||
|
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", null); |
||||
assertThatExceptionOfType(RuntimeException.class) |
||||
.isThrownBy(() -> this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch)); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue