Revert "Spring Boot 3 migration (#255)"
This reverts commit aa5d636de3.
This commit is contained in:
@@ -5,19 +5,20 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>releaser-projects</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-parent</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud</module>
|
||||
<module>spring-cloud-stream</module>
|
||||
<module>reactor</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
||||
0
projects/reactor/.jdk8
Normal file
0
projects/reactor/.jdk8
Normal file
107
projects/reactor/pom.xml
Normal file
107
projects/reactor/pom.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>reactor</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-projects</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<cloudfoundry-client.version>5.7.0.RELEASE</cloudfoundry-client.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>cloudfoundry-client-reactor</artifactId>
|
||||
<version>${cloudfoundry-client.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cloudfoundry</groupId>
|
||||
<artifactId>cloudfoundry-operations</artifactId>
|
||||
<version>${cloudfoundry-client.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>sonar</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-unit-test</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<propertyName>surefireArgLine</propertyName>
|
||||
<destFile>${project.build.directory}/jacoco.exec</destFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>post-unit-test</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<!-- Sets the path to the file which contains the execution data. -->
|
||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Sets the VM argument line used when unit tests are run. -->
|
||||
<argLine>${surefireArgLine}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 releaser;
|
||||
|
||||
import releaser.internal.options.Parser;
|
||||
import releaser.internal.spring.ExecutionResultHandler;
|
||||
import releaser.internal.spring.SpringReleaser;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ReleaserApplication extends ReleaserCommandLineRunner {
|
||||
|
||||
public ReleaserApplication(SpringReleaser releaser, ExecutionResultHandler executionResultHandler, Parser parser) {
|
||||
super(releaser, executionResultHandler, parser);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication application = new SpringApplication(ReleaserApplication.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 releaser.reactor;
|
||||
|
||||
import org.cloudfoundry.client.CloudFoundryClient;
|
||||
import org.cloudfoundry.doppler.DopplerClient;
|
||||
import org.cloudfoundry.operations.DefaultCloudFoundryOperations;
|
||||
import org.cloudfoundry.reactor.ConnectionContext;
|
||||
import org.cloudfoundry.reactor.DefaultConnectionContext;
|
||||
import org.cloudfoundry.reactor.TokenProvider;
|
||||
import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient;
|
||||
import org.cloudfoundry.reactor.doppler.ReactorDopplerClient;
|
||||
import org.cloudfoundry.reactor.tokenprovider.PasswordGrantTokenProvider;
|
||||
import org.cloudfoundry.reactor.uaa.ReactorUaaClient;
|
||||
import org.cloudfoundry.uaa.UaaClient;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
/**
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("production")
|
||||
class CfConfiguration {
|
||||
|
||||
@Bean
|
||||
DefaultConnectionContext connectionContext(@Value("${cf.apiHost}") String apiHost) {
|
||||
return DefaultConnectionContext.builder().apiHost(apiHost).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordGrantTokenProvider tokenProvider(@Value("${cf.username}") String username,
|
||||
@Value("${cf.password}") String password) {
|
||||
return PasswordGrantTokenProvider.builder().password(password).username(username).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactorCloudFoundryClient cloudFoundryClient(ConnectionContext connectionContext, TokenProvider tokenProvider) {
|
||||
return ReactorCloudFoundryClient.builder().connectionContext(connectionContext).tokenProvider(tokenProvider)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactorDopplerClient dopplerClient(ConnectionContext connectionContext, TokenProvider tokenProvider) {
|
||||
return ReactorDopplerClient.builder().connectionContext(connectionContext).tokenProvider(tokenProvider).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactorUaaClient uaaClient(ConnectionContext connectionContext, TokenProvider tokenProvider) {
|
||||
return ReactorUaaClient.builder().connectionContext(connectionContext).tokenProvider(tokenProvider).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
DefaultCloudFoundryOperations defaultCloudFoundryOperations(CloudFoundryClient cloudFoundryClient,
|
||||
DopplerClient dopplerClient, UaaClient uaaClient, @Value("${cf.organization}") String organizationId,
|
||||
@Value("${cf.space}") String spaceId) {
|
||||
return DefaultCloudFoundryOperations.builder().cloudFoundryClient(cloudFoundryClient)
|
||||
.dopplerClient(dopplerClient).uaaClient(uaaClient).organization(organizationId).space(spaceId).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 releaser.reactor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonValue;
|
||||
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Issue;
|
||||
import com.jcabi.github.Issues;
|
||||
import com.jcabi.github.Release;
|
||||
import com.jcabi.github.Repo;
|
||||
import com.jcabi.github.RepoCommit;
|
||||
import com.jcabi.github.RepoCommits;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import releaser.internal.git.ProjectGitHandler;
|
||||
import releaser.internal.git.SimpleCommit;
|
||||
import releaser.internal.spring.Arguments;
|
||||
import releaser.internal.tasks.DryRunReleaseReleaserTask;
|
||||
import releaser.internal.tasks.ProjectPostReleaseReleaserTask;
|
||||
import releaser.internal.tasks.release.PushChangesReleaseTask;
|
||||
import releaser.internal.tech.BuildUnstableException;
|
||||
import releaser.internal.tech.ExecutionResult;
|
||||
|
||||
/**
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class GenerateReleaseNotesTask implements ProjectPostReleaseReleaserTask, DryRunReleaseReleaserTask {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GenerateReleaseNotesTask.class);
|
||||
|
||||
private static final String NAME = "releaseNotes";
|
||||
|
||||
private static final String SHORTNAME = "rn";
|
||||
|
||||
private final Github github;
|
||||
|
||||
private final ProjectGitHandler gitHandler;
|
||||
|
||||
public GenerateReleaseNotesTask(Github github, ProjectGitHandler gitHandler) {
|
||||
this.github = github;
|
||||
this.gitHandler = gitHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return PushChangesReleaseTask.ORDER + 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String shortName() {
|
||||
return SHORTNAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String header() {
|
||||
return "Generating release notes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Generates the release notes as a Github release draft, from issues and commits";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionResult runTask(Arguments args) throws BuildUnstableException, RuntimeException {
|
||||
if (args.versionFromBom.isSnapshot()) {
|
||||
log.info("\nWon't generate notes for a snapshot version");
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
|
||||
Repo repo = github.repos()
|
||||
.get(new Coordinates.Simple(args.properties.getGit().getOrgName(), args.projectToRun.name()));
|
||||
|
||||
String releaseTag = "v" + args.versionFromBom.version;
|
||||
if (args.options.dryRun != Boolean.TRUE) {
|
||||
Optional<String> maybeTagSha1 = gitHandler.findTagSha1(args.project, releaseTag);
|
||||
if (maybeTagSha1.isPresent()) {
|
||||
String tagSha1 = maybeTagSha1.get();
|
||||
try {
|
||||
// call json() to trigger fetch of gh tag by SHA1
|
||||
repo.git().references().get("tags/" + tagSha1).json();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return ExecutionResult.failure(new IllegalStateException("Shouldn't create a release if tag "
|
||||
+ releaseTag + " (sha1=" + tagSha1 + ") not visible in Github", e));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return ExecutionResult.failure(new IllegalStateException(
|
||||
"Attempting to draft release note but tag not found in repository: " + releaseTag));
|
||||
}
|
||||
}
|
||||
|
||||
Issues issuesClient = repo.issues();
|
||||
EnumMap<Type, List<ChangelogEntry>> entries = new EnumMap<>(Type.class);
|
||||
|
||||
String toVersionTag = "v" + args.versionFromBom.version;
|
||||
if (args.options.dryRun == Boolean.TRUE && !gitHandler.findTagSha1(args.project, toVersionTag).isPresent()) {
|
||||
toVersionTag = "HEAD";
|
||||
}
|
||||
|
||||
// eg. 3.2.1 for current 3.2.2
|
||||
String fromVersionTag = args.versionFromBom.computePreviousPatchTag("v", "RELEASE").orElseGet(() -> {
|
||||
Pattern pattern = args.versionFromBom.computePreviousMinorTagPattern("v", "RELEASE")
|
||||
.orElseGet(() -> args.versionFromBom.computePreviousMajorTagPattern("v", "RELEASE"));
|
||||
// eg. v3.2.*.RELEASE or v3.*.*.RELEASE
|
||||
log.info("Couldn't simply compute previous version of {}, looking through tag list with pattern {}",
|
||||
args.versionFromBom.version, pattern.pattern());
|
||||
|
||||
List<String> sortedTags = gitHandler.findTagNamesMatching(args.project, pattern)
|
||||
.collect(Collectors.toList());
|
||||
if (sortedTags.isEmpty()) {
|
||||
throw new IllegalStateException("Couldn't find a tag that matches pattern " + pattern.pattern());
|
||||
}
|
||||
sortedTags.forEach(System.out::println);
|
||||
return sortedTags.get(0);
|
||||
});
|
||||
|
||||
if (Boolean.TRUE == args.options.interactive) {
|
||||
log.info("\nComputed log range is {}..{}", fromVersionTag, toVersionTag);
|
||||
log.info("\nForce the FROM in log range if needed [{}]", fromVersionTag);
|
||||
String modifiedFrom = System.console().readLine();
|
||||
if (!modifiedFrom.trim().isEmpty()) {
|
||||
fromVersionTag = modifiedFrom;
|
||||
}
|
||||
log.info("\nForce the TO in log range if needed [{}]", toVersionTag);
|
||||
String modifiedTo = System.console().readLine();
|
||||
if (!modifiedTo.trim().isEmpty()) {
|
||||
toVersionTag = modifiedTo;
|
||||
}
|
||||
}
|
||||
log.info("Will fetch the log for range {}..{}", fromVersionTag, toVersionTag);
|
||||
|
||||
// gather commits. we use tags in the format `vVERSION`
|
||||
final List<SimpleCommit> revCommits = gitHandler.commitsBetween(args.project, fromVersionTag, toVersionTag);
|
||||
|
||||
// parse and link to issues if possible, determining type
|
||||
for (SimpleCommit revCommit : revCommits) {
|
||||
ChangelogEntry entry = parseChangeLogEntry(issuesClient, revCommit);
|
||||
|
||||
for (Type type : entry.types) {
|
||||
entries.computeIfAbsent(type, t -> new ArrayList<>()).add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// generate the notes
|
||||
String notes = generateNotes(args, entries, extractContributorMentions(repo, revCommits));
|
||||
|
||||
if (args.options.dryRun != null && args.options.dryRun) {
|
||||
// print out
|
||||
log.info("[Dry-Run] Generated release notes:");
|
||||
log.info("\n\n" + notes + "\n\n");
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
else {
|
||||
// WARNING: double check the tag actually exists, otherwise this will create a
|
||||
// tag :( The verification is done early in the task to avoid fetching
|
||||
// commits/issues
|
||||
|
||||
Release.Smart draftRelease = null;
|
||||
JsonObject draftReleaseJson = null;
|
||||
try {
|
||||
// attempt to find gh release with this tag. stop at 2 month old releases
|
||||
Instant oldestReleaseToConsider = ZonedDateTime.now().minusMonths(2).toInstant();
|
||||
for (Release release : repo.releases().iterate()) {
|
||||
JsonObject releaseJson = release.json();
|
||||
// seems the created release has a wrong tag "untagged-xxxxx", we also
|
||||
// look at name
|
||||
if (releaseJson.getString("tag_name").equals(releaseTag)
|
||||
|| releaseJson.getString("name").equals(releaseTag)) {
|
||||
if (!releaseJson.getBoolean("draft")) {
|
||||
return ExecutionResult
|
||||
.failure(new IllegalStateException("Release already exists for tag " + releaseTag));
|
||||
}
|
||||
else {
|
||||
draftRelease = new Release.Smart(release);
|
||||
draftReleaseJson = releaseJson;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// we didn't find a matching release. don't go too far back in time!
|
||||
String publishedAtJson = releaseJson.getString("published_at");
|
||||
Instant publishedAt = oldestReleaseToConsider.minusSeconds(1);
|
||||
if (publishedAtJson != null) {
|
||||
publishedAt = ZonedDateTime.parse(publishedAtJson).toInstant();
|
||||
}
|
||||
if (publishedAt.isBefore(oldestReleaseToConsider)) {
|
||||
log.info("OK, didn't find a release matching tag " + releaseTag
|
||||
+ " within the last 2 month, stopping there");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return ExecutionResult.failure(
|
||||
new IllegalStateException("Unable to try to match github releases with tag " + releaseTag, e));
|
||||
}
|
||||
|
||||
if (draftReleaseJson == null) {
|
||||
// create a draft release for the tag
|
||||
try {
|
||||
Release.Smart release = new Release.Smart(repo.releases().create(releaseTag));
|
||||
release.draft(true);
|
||||
release.tag(releaseTag);
|
||||
release.name(releaseTag);
|
||||
release.body(notes);
|
||||
if (args.versionFromBom.isMilestone() || args.versionFromBom.isRc()) {
|
||||
release.prerelease(true);
|
||||
}
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return ExecutionResult.failure(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// update the existing draft
|
||||
try {
|
||||
// edit the release
|
||||
String oldAndNewNotes = draftReleaseJson.getString("body") + "\n\n----\nNew draft added "
|
||||
+ LocalDateTime.now().toString() + "\n----\n" + notes;
|
||||
draftRelease.body(oldAndNewNotes);
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return ExecutionResult.failure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the release notes.
|
||||
*/
|
||||
protected String generateNotes(Arguments args, EnumMap<Type, List<ChangelogEntry>> entries,
|
||||
List<String> contributorGithubMentions) {
|
||||
// TODO use a template? handlebars !
|
||||
StringBuilder notes = new StringBuilder().append(args.projectToRun.name()).append(" `")
|
||||
.append(args.versionFromBom.version).append("` is part of **`").append(args.releaseTrain().version)
|
||||
.append("` Release Train**.");
|
||||
|
||||
notes.append("\n\n## :warning: Update considerations and deprecations");
|
||||
for (ChangelogEntry noteworthy : entries.getOrDefault(Type.NOTEWORTHY, Collections.emptyList())) {
|
||||
notes.append("\n - ").append(noteworthy.description);
|
||||
for (String issueTitle : noteworthy.associatedIssueLinksAndTitles.values()) {
|
||||
notes.append("\n\t - ").append(cleanupShortMessage(issueTitle));
|
||||
}
|
||||
}
|
||||
|
||||
notes.append("\n\n## :sparkles: New features and improvements");
|
||||
for (ChangelogEntry feature : entries.getOrDefault(Type.FEATURE, Collections.emptyList())) {
|
||||
notes.append("\n - ").append(feature.description);
|
||||
for (String issueTitle : feature.associatedIssueLinksAndTitles.values()) {
|
||||
notes.append("\n\t - ").append(cleanupShortMessage(issueTitle));
|
||||
}
|
||||
}
|
||||
|
||||
notes.append("\n\n## :beetle: Bug fixes");
|
||||
for (ChangelogEntry bug : entries.getOrDefault(Type.BUG, Collections.emptyList())) {
|
||||
notes.append("\n - ").append(bug.description);
|
||||
for (String issueTitle : bug.associatedIssueLinksAndTitles.values()) {
|
||||
notes.append("\n\t - ").append(cleanupShortMessage(issueTitle));
|
||||
}
|
||||
}
|
||||
|
||||
notes.append("\n\n## :book: Documentation, Tests and Build");
|
||||
for (ChangelogEntry misc : entries.getOrDefault(Type.DOC_MISC, Collections.emptyList())) {
|
||||
notes.append("\n - ").append(misc.description);
|
||||
for (String issueTitle : misc.associatedIssueLinksAndTitles.values()) {
|
||||
notes.append("\n\t - ").append(cleanupShortMessage(issueTitle));
|
||||
}
|
||||
}
|
||||
|
||||
notes.append("\n\n## **TODO DISPATCH THESE**");
|
||||
for (ChangelogEntry unclassified : entries.getOrDefault(Type.UNCLASSIFIED, Collections.emptyList())) {
|
||||
notes.append("\n - ").append(unclassified.description);
|
||||
for (String issueTitle : unclassified.associatedIssueLinksAndTitles.values()) {
|
||||
notes.append("\n\t - ").append(cleanupShortMessage(issueTitle));
|
||||
}
|
||||
}
|
||||
|
||||
// contributors
|
||||
notes.append("\n\n## :+1: Thanks to the following contributors that also participated to this release\n");
|
||||
notes.append(String.join(", ", contributorGithubMentions));
|
||||
|
||||
return notes.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize into a {@link Type} from a set of labels and the commit's short message
|
||||
* (eg. in case of specific message prefix).
|
||||
* @param labels the set of labels found for issues referenced in the commit
|
||||
* @param shortMessage the commit's title/short message
|
||||
* @return a {@link Type} categorizing the commit, or {@link Type#UNCLASSIFIED} if not
|
||||
* clear
|
||||
*/
|
||||
protected EnumSet<Type> extractTypes(Set<String> labels, String shortMessage) {
|
||||
List<Type> types = new ArrayList<>();
|
||||
for (String label : labels) {
|
||||
switch (label) {
|
||||
case "type/bug":
|
||||
types.add(Type.BUG);
|
||||
break;
|
||||
case "type/enhancement":
|
||||
types.add(Type.FEATURE);
|
||||
break;
|
||||
case "type/documentation":
|
||||
case "type/dependency-upgrade":
|
||||
case "type/chores":
|
||||
types.add(Type.DOC_MISC);
|
||||
break;
|
||||
default:
|
||||
if (label.startsWith("warn/")) {
|
||||
types.add(Type.NOTEWORTHY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shortMessage.startsWith("[build]") || shortMessage.startsWith("[polish]")
|
||||
|| shortMessage.startsWith("[doc]")) {
|
||||
types.add(Type.DOC_MISC);
|
||||
}
|
||||
|
||||
if (types.isEmpty()) {
|
||||
return EnumSet.of(Type.UNCLASSIFIED);
|
||||
}
|
||||
return EnumSet.copyOf(types);
|
||||
}
|
||||
|
||||
protected String commitToGithubMention(RepoCommits commitsClient, SimpleCommit revCommit) {
|
||||
RepoCommit dumbCommit = commitsClient.get(revCommit.fullSha1);
|
||||
RepoCommit.Smart smartCommit = new RepoCommit.Smart(dumbCommit);
|
||||
try {
|
||||
JsonObject commitJson = smartCommit.json();
|
||||
JsonValue.ValueType authorType = commitJson.get("author").getValueType();
|
||||
if (authorType == JsonValue.ValueType.OBJECT) {
|
||||
return "@" + commitJson.getJsonObject("author").getString("login");
|
||||
}
|
||||
else if (authorType == JsonValue.ValueType.NULL) {
|
||||
// assume author+committer, look under commit.author.name
|
||||
if (commitJson.containsKey("commit") && commitJson.getJsonObject("commit").containsKey("author")) {
|
||||
return "@" + commitJson.getJsonObject("commit").getJsonObject("author").getString("name");
|
||||
}
|
||||
}
|
||||
// in case unexpected json, output the "sha", "commit", "author" and
|
||||
// "committer"
|
||||
return "@RAW{\"sha\", " + commitJson.get("sha") + ", \"author\", \"" + commitJson.get("author")
|
||||
+ ", \"committer\", \"" + commitJson.get("committer") + ", \"commit\", \""
|
||||
+ commitJson.get("commit") + "}";
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract at-mentions of contributors, given a list of commits (will fetch
|
||||
* contributor login from github). The list is deduplicated and sorted in
|
||||
* case-insensitive alphabetical order.
|
||||
*/
|
||||
List<String> extractContributorMentions(Repo repo, List<SimpleCommit> revCommits) {
|
||||
RepoCommits commitsClient = repo.commits();
|
||||
return revCommits.stream().map(c -> commitToGithubMention(commitsClient, c)).filter(Objects::nonNull).distinct()
|
||||
.sorted(String.CASE_INSENSITIVE_ORDER).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up a short message, removing issue links in suffix and pr prefix forms.
|
||||
*/
|
||||
protected String cleanupShortMessage(String shortMessage) {
|
||||
final Matcher shortMessageMatcher = SHORT_MESSAGE.matcher(shortMessage);
|
||||
if (shortMessageMatcher.matches()) {
|
||||
return shortMessageMatcher.group(1).trim();
|
||||
}
|
||||
return shortMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the short message of a {@link SimpleCommit}, ie detect message prefix like
|
||||
* "fix #123 Something something (#124)". The prefix up to the issue link is removed,
|
||||
* and so is the PR reference at the end.
|
||||
*/
|
||||
protected String cleanupShortMessage(SimpleCommit commit) {
|
||||
if (!commit.isMergeCommit) {
|
||||
return cleanupShortMessage(commit.title);
|
||||
}
|
||||
return commit.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract issue numbers from links like #123 in the whole commit message, in order.
|
||||
*/
|
||||
protected Set<Integer> extractIssueNumbers(SimpleCommit commit) {
|
||||
Set<Integer> issueNumbers = new LinkedHashSet<>();
|
||||
Matcher titleIssueMatcher = ISSUE_REF.matcher(commit.title);
|
||||
while (titleIssueMatcher.find()) {
|
||||
issueNumbers.add(Integer.valueOf(titleIssueMatcher.group(1)));
|
||||
}
|
||||
Matcher bodyIssueMatcher = ISSUE_REF.matcher(commit.fullMessage);
|
||||
while (bodyIssueMatcher.find()) {
|
||||
issueNumbers.add(Integer.valueOf(bodyIssueMatcher.group(1)));
|
||||
}
|
||||
return issueNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* From the set of issue numbers (see {@link #extractIssueNumbers(SimpleCommit)}),
|
||||
* extract github client Issue objects and fetch labels and titles from these issues.
|
||||
* The labels and titles are injected in a {@link Set} of labels and {@link Map} of
|
||||
* hashtag issue links to issue titles.
|
||||
*/
|
||||
protected void fetchIssueLabelsAndTitles(Issues issueClient, Set<Integer> issueNumbers, Set<String> labelsTarget,
|
||||
Map<String, String> referencedIssuesTarget) {
|
||||
issueNumbers.forEach(i -> {
|
||||
try {
|
||||
// avoid "Smart" issue here as it makes further requests to the server :(
|
||||
// we have everything handy in the JSON
|
||||
Issue issue = issueClient.get(i);
|
||||
JsonObject issueJson = issue.json();
|
||||
issueJson.getJsonArray("labels")
|
||||
.forEach(label -> labelsTarget.add(((JsonObject) label).getString("name")));
|
||||
referencedIssuesTarget.put("#" + issueJson.getInt("number"), issueJson.getString("title"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("Could not fetch issue information for #" + i, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract issue information from the commit and generate the {@link ChangelogEntry}.
|
||||
*/
|
||||
protected ChangelogEntry parseChangeLogEntry(Issues issueClient, SimpleCommit commit) {
|
||||
String cleanShortMessage = cleanupShortMessage(commit);
|
||||
Set<Integer> issueNumbers = extractIssueNumbers(commit);
|
||||
|
||||
Set<String> labelsTarget = new HashSet<>();
|
||||
Map<String, String> referencedIssuesTarget = new LinkedHashMap<>();
|
||||
fetchIssueLabelsAndTitles(issueClient, issueNumbers, labelsTarget, referencedIssuesTarget);
|
||||
|
||||
EnumSet<Type> types = extractTypes(labelsTarget, commit.title);
|
||||
|
||||
return new ChangelogEntry(types, commit.abbreviatedSha1, cleanShortMessage, referencedIssuesTarget);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pattern specific to reactor commit message convention: `fix #123 Some short
|
||||
* description (#4567)`. We want to capture the middle part and use that in the
|
||||
* release note. The `fix` part is optional, but occurs most of the time unless there
|
||||
* is no issue associated. The `(#xxx)` part is also optional and much more rare,
|
||||
* reflecting the PR number in case there has been extensive review/discussion in that
|
||||
* PR. Both references would be caught by ISSUE_REF pattern below.
|
||||
*/
|
||||
static Pattern SHORT_MESSAGE = Pattern.compile("(?:[a-zA-Z]+ #[0-9]+)?([^\\(]+)(?:\\(#[0-9]+\\))?");
|
||||
|
||||
/**
|
||||
* A {@link Pattern} to find issue/pr numbers in a commit message, by detecting
|
||||
* {@code #}.
|
||||
*/
|
||||
protected static final Pattern ISSUE_REF = Pattern.compile("#([0-9]+)");
|
||||
|
||||
/**
|
||||
* An enum of the 3 types of changes recognized by the changelog template, plus one
|
||||
* type for noteworthy items (eg. breaking changes) and one additional type for the
|
||||
* commits that couldn't be classified (eg. no relevant prefix and no associated
|
||||
* issue).
|
||||
*/
|
||||
protected enum Type {
|
||||
|
||||
NOTEWORTHY, BUG, FEATURE, DOC_MISC, UNCLASSIFIED;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of an entry in the release notes changelog.
|
||||
*/
|
||||
protected static final class ChangelogEntry {
|
||||
|
||||
/**
|
||||
* The set of types of the change, or singleton {@link Type#UNCLASSIFIED} if
|
||||
* unknown.
|
||||
*/
|
||||
public final EnumSet<Type> types;
|
||||
|
||||
/**
|
||||
* The human-friendly description to put in the release note for that commit.
|
||||
*/
|
||||
public final String description;
|
||||
|
||||
/**
|
||||
* The human-friendly SHA1 (typically abbreviated to 8 chars) to reference the
|
||||
* commit in the release note if there is no associated issue.
|
||||
*/
|
||||
public final String commitSha1;
|
||||
|
||||
/**
|
||||
* An ordered map of github-compatible issue links (including the pound sign) and
|
||||
* their titles, to be added to the draft as possible alternative descriptions.
|
||||
* The links are automatically added to the end of the {@link #description} by the
|
||||
* {@link #ChangelogEntry(EnumSet, String, String, Map)} constructor}.
|
||||
*/
|
||||
public final Map<String, String> associatedIssueLinksAndTitles;
|
||||
|
||||
/**
|
||||
* @param types the set of {@link Type} of the commit
|
||||
* @param commitSha1 the commit's human-friendly sha1 (can and should be the
|
||||
* abbreviated form)
|
||||
* @param cleanShortMessage the commit's cleaned up title, as should be displayed
|
||||
* in the release note entry
|
||||
* @param associatedIssueLinksAndTitles the commit's associated
|
||||
* issue(s)/pull-request(s), in order of importance. If non-empty, each issue will
|
||||
* be mentioned at the end of the entry, and the issue titles will be added to the
|
||||
* {@link #description} on their own lines as potential alternative descriptions
|
||||
*/
|
||||
ChangelogEntry(EnumSet<Type> types, String commitSha1, String cleanShortMessage,
|
||||
Map<String, String> associatedIssueLinksAndTitles) {
|
||||
this.types = types;
|
||||
this.commitSha1 = commitSha1;
|
||||
this.associatedIssueLinksAndTitles = associatedIssueLinksAndTitles;
|
||||
|
||||
if (this.associatedIssueLinksAndTitles.isEmpty()) {
|
||||
description = cleanShortMessage + " (" + commitSha1 + ")";
|
||||
}
|
||||
else {
|
||||
this.description = cleanShortMessage + " ("
|
||||
+ String.join(", ", this.associatedIssueLinksAndTitles.keySet()) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 releaser.reactor;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.RtGithub;
|
||||
import com.jcabi.http.wire.RetryWire;
|
||||
import org.cloudfoundry.operations.CloudFoundryOperations;
|
||||
import releaser.internal.Releaser;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.git.ProjectGitHandler;
|
||||
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Configuration
|
||||
@Profile("production")
|
||||
class ReactorConfiguration {
|
||||
|
||||
@Bean
|
||||
CfClient cfClient(CloudFoundryOperations cloudFoundryOperations) {
|
||||
return new CfClient(cloudFoundryOperations);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestartSiteProjectPostReleaseTask restartSiteProjectPostReleaseTask(Releaser releaser, CfClient cfClient,
|
||||
@Value("${cf.reactorAppName}") String reactorAppName) {
|
||||
return new RestartSiteProjectPostReleaseTask(releaser, cfClient, reactorAppName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Github githubClient(ReleaserProperties properties) {
|
||||
if (!StringUtils.hasText(properties.getGit().getOauthToken())) {
|
||||
throw new BeanInitializationException("You must set the value of the OAuth token. You can do it "
|
||||
+ "either via the command line [--releaser.git.oauth-token=...] "
|
||||
+ "or put it as an env variable in [~/.bashrc] or "
|
||||
+ "[~/.zshrc] e.g. [export RELEASER_GIT_OAUTH_TOKEN=...]");
|
||||
}
|
||||
return new RtGithub(new RtGithub(properties.getGit().getOauthToken()).entry().through(RetryWire.class));
|
||||
}
|
||||
|
||||
@Bean
|
||||
GenerateReleaseNotesTask releaseNotesTask(Github github, ProjectGitHandler gitHandler) {
|
||||
return new GenerateReleaseNotesTask(github, gitHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 releaser.reactor;
|
||||
|
||||
import org.cloudfoundry.operations.CloudFoundryOperations;
|
||||
import org.cloudfoundry.operations.applications.RestartApplicationRequest;
|
||||
import releaser.internal.Releaser;
|
||||
import releaser.internal.spring.Arguments;
|
||||
import releaser.internal.tasks.release.PublishDocsReleaseTask;
|
||||
import releaser.internal.tech.ExecutionResult;
|
||||
|
||||
public class RestartSiteProjectPostReleaseTask extends PublishDocsReleaseTask {
|
||||
|
||||
private static final String REACTOR_CORE_PROJECT_NAME = "reactor-core";
|
||||
|
||||
private final CfClient cfClient;
|
||||
|
||||
private final String reactorAppName;
|
||||
|
||||
public RestartSiteProjectPostReleaseTask(Releaser releaser, CfClient cfClient, String reactorAppName) {
|
||||
super(releaser);
|
||||
this.cfClient = cfClient;
|
||||
this.reactorAppName = reactorAppName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionResult runTask(Arguments args) {
|
||||
if (!REACTOR_CORE_PROJECT_NAME.equals(args.projectToRun.name())) {
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
this.cfClient.restartApp(this.reactorAppName);
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CfClient {
|
||||
|
||||
private final CloudFoundryOperations cloudFoundryOperations;
|
||||
|
||||
CfClient(CloudFoundryOperations cloudFoundryOperations) {
|
||||
this.cloudFoundryOperations = cloudFoundryOperations;
|
||||
}
|
||||
|
||||
void restartApp(String name) {
|
||||
this.cloudFoundryOperations.applications().restart(RestartApplicationRequest.builder().name(name).build());
|
||||
}
|
||||
|
||||
}
|
||||
38
projects/reactor/src/main/resources/application.yml
Normal file
38
projects/reactor/src/main/resources/application.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
spring:
|
||||
main:
|
||||
web-application-type: none
|
||||
datasource:
|
||||
url: jdbc:h2:mem:${random.uuid}
|
||||
jackson:
|
||||
deserialization:
|
||||
FAIL_ON_UNKNOWN_PROPERTIES: true
|
||||
profiles:
|
||||
active: production
|
||||
releaser:
|
||||
git:
|
||||
org-name: reactor
|
||||
release-train-bom-url: https://github.com/reactor/reactor
|
||||
fetch-versions-from-git: true
|
||||
gradle:
|
||||
build-command: "./gradlew clean bumpVersionsInReadme build publishToMavenLocal --console=plain -PnextVersion={{nextVersion}} -PoldVersion={{oldVersion}} -PcurrentVersion={{version}} {{systemProps}}"
|
||||
|
||||
meta-release:
|
||||
release-train-project-name: reactor
|
||||
release-train-dependency-names:
|
||||
- reactor
|
||||
git-org-url: https://github.com/reactor
|
||||
cf:
|
||||
organization: FrameworksAndRuntimes
|
||||
space: Reactor
|
||||
reactorAppName: projectreactor
|
||||
apiHost: api.run.pivotal.io
|
||||
|
||||
# Boot values to be passed via env/command line:
|
||||
# cf.username
|
||||
# cf.password
|
||||
|
||||
# Gradle project properties to be passed to deploy task somehow
|
||||
# artifactory_publish_contextUrl
|
||||
# artifactory_publish_repoKey
|
||||
# artifactory_publish_username
|
||||
# artifactory_publish_password
|
||||
46
projects/reactor/src/main/resources/logback.xml
Normal file
46
projects/reactor/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
~ Copyright 2013-2020 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.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
|
||||
<appender name="COMMANDFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<!-- <file>${java.io.tmpdir}/reactor-releaser-commands.log</file>-->
|
||||
<file>logs/reactor-releaser-commands.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||
<!-- Keep 3 releases worth of history -->
|
||||
<fileNamePattern>logs/reactor-releaser-commands.%i.log</fileNamePattern>
|
||||
<minIndex>1</minIndex>
|
||||
<maxIndex>3</maxIndex>
|
||||
</rollingPolicy>
|
||||
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<maxFileSize>5MB</maxFileSize>
|
||||
</triggeringPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="releaser.commands" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="COMMANDFILE"/>
|
||||
</logger>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="COMMANDFILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 releaser;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import releaser.internal.tasks.DryRunReleaseReleaserTask;
|
||||
import releaser.internal.tasks.ReleaserTask;
|
||||
import releaser.internal.tasks.release.PublishDocsReleaseTask;
|
||||
import releaser.reactor.GenerateReleaseNotesTask;
|
||||
import releaser.reactor.RestartSiteProjectPostReleaseTask;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = { ReleaserApplication.class })
|
||||
@ActiveProfiles("test")
|
||||
class ReleaserApplicationTests {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context;
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_load_generate_release_notes_in_dry_run() {
|
||||
Map<String, DryRunReleaseReleaserTask> beans = context.getBeansOfType(DryRunReleaseReleaserTask.class);
|
||||
List<ReleaserTask> inOrder = new LinkedList<>(beans.values());
|
||||
inOrder.sort(AnnotationAwareOrderComparator.INSTANCE);
|
||||
|
||||
assertThat(inOrder).anySatisfy(task -> assertThat(task).isInstanceOf(GenerateReleaseNotesTask.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_load_restart_site() {
|
||||
Map<String, ReleaserTask> beans = context.getBeansOfType(ReleaserTask.class);
|
||||
List<ReleaserTask> inOrder = new LinkedList<>(beans.values());
|
||||
inOrder.sort(AnnotationAwareOrderComparator.INSTANCE);
|
||||
|
||||
assertThat(inOrder).anySatisfy(task -> assertThat(task).isInstanceOf(RestartSiteProjectPostReleaseTask.class));
|
||||
assertThat(inOrder).noneSatisfy(task -> assertThat(task).isInstanceOf(PublishDocsReleaseTask.class)
|
||||
.isNotInstanceOf(RestartSiteProjectPostReleaseTask.class));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 releaser.reactor;
|
||||
|
||||
import org.cloudfoundry.operations.CloudFoundryOperations;
|
||||
import org.mockito.BDDMockito;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
/**
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
class CfTestConfiguration {
|
||||
|
||||
@Bean
|
||||
CloudFoundryOperations mockCloudFoundryOperations() {
|
||||
return BDDMockito.mock(CloudFoundryOperations.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 releaser.reactor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.jcabi.github.Issue;
|
||||
import com.jcabi.github.Issues;
|
||||
import com.jcabi.github.mock.MkGithub;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.VoidAnswer4;
|
||||
import releaser.internal.git.SimpleCommit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.AdditionalAnswers.answerVoid;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static releaser.reactor.GenerateReleaseNotesTask.Type;
|
||||
|
||||
/**
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
class GenerateReleaseNotesTaskTest {
|
||||
|
||||
@Autowired
|
||||
GenerateReleaseNotesTask task;
|
||||
|
||||
@Autowired
|
||||
MkGithub githubClient;
|
||||
|
||||
@Test
|
||||
void extractTypeFromLabel() {
|
||||
assertThat(task.extractTypes(Collections.singleton("type/bug"), "")).as("type/bug").containsOnly(Type.BUG);
|
||||
|
||||
assertThat(task.extractTypes(Collections.singleton("type/enhancement"), "")).as("type/enhancement")
|
||||
.containsOnly(Type.FEATURE);
|
||||
|
||||
assertThat(task.extractTypes(Collections.singleton("type/documentation"), "")).as("type/documentation")
|
||||
.containsOnly(Type.DOC_MISC);
|
||||
|
||||
assertThat(task.extractTypes(Collections.singleton("type/chores"), "")).as("type/chores")
|
||||
.containsOnly(Type.DOC_MISC);
|
||||
|
||||
assertThat(task.extractTypes(Collections.singleton("warn/something"), "")).as("warn/*")
|
||||
.containsOnly(Type.NOTEWORTHY);
|
||||
|
||||
assertThat(task.extractTypes(Collections.singleton("type/whatever"), "")).as("type/whatever")
|
||||
.containsOnly(Type.UNCLASSIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractTypeFromMultipleLabels() {
|
||||
Set<String> labels = new LinkedHashSet<>();
|
||||
labels.add("type/bug");
|
||||
labels.add("type/enhancement");
|
||||
labels.add("type/documentation");
|
||||
labels.add("whatever");
|
||||
|
||||
assertThat(task.extractTypes(labels, "")).as("multiple labels").containsOnly(Type.BUG, Type.FEATURE,
|
||||
Type.DOC_MISC);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractMiscTypeFromBuildMessagePrefix() {
|
||||
String message = "[build] Foo";
|
||||
|
||||
assertThat(task.extractTypes(Collections.emptySet(), message)).containsOnly(Type.DOC_MISC);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractMiscTypeFromPolishMessagePrefix() {
|
||||
String message = "[polish] Foo";
|
||||
|
||||
assertThat(task.extractTypes(Collections.emptySet(), message)).containsOnly(Type.DOC_MISC);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractMiscTypeFromDocMessagePrefix() {
|
||||
String message = "[doc] Foo";
|
||||
|
||||
assertThat(task.extractTypes(Collections.emptySet(), message)).containsOnly(Type.DOC_MISC);
|
||||
}
|
||||
|
||||
@Test
|
||||
void extractUnclassifiedTypeFromRandomMessagePrefix() {
|
||||
String message = "fix #123 There was a [bug], needed to [polish] the [doc]";
|
||||
|
||||
assertThat(task.extractTypes(Collections.emptySet(), message)).containsOnly(Type.UNCLASSIFIED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTitleCleanupFromMergeCommit() {
|
||||
SimpleCommit mergeCommit = new SimpleCommit("sha1", "fullsha1", "merge #123 into 3.3 (#123)",
|
||||
"merge #123 into 3.3", "Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", true);
|
||||
|
||||
assertThat(task.cleanupShortMessage(mergeCommit)).isEqualTo("merge #123 into 3.3 (#123)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleCleanupFixPrefix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "fix #123 Text from title",
|
||||
"fix #123 Some more text", "Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io",
|
||||
false);
|
||||
|
||||
assertThat(task.cleanupShortMessage(commit)).isEqualTo("Text from title");
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleCleanupSeePrefix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "see #123 Text from title",
|
||||
"see #123 Some more text", "Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io",
|
||||
false);
|
||||
|
||||
assertThat(task.cleanupShortMessage(commit)).isEqualTo("Text from title");
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleCleanupPrStyleSuffix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "Commit without issue (#123)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.cleanupShortMessage(commit)).isEqualTo("Commit without issue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleCleanupPrStyleSuffixNoSpace() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "Commit without issue(#123)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.cleanupShortMessage(commit)).isEqualTo("Commit without issue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleCleanupBothPrefixAndSuffix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title (#123)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.cleanupShortMessage(commit)).isEqualTo("Commit title");
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueNumberTitlePrefix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.extractIssueNumbers(commit)).containsOnly(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueNumberTitlePrStyleSuffix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "Commit title (#123)", "fullMessage", "Simon Baslé",
|
||||
"sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.extractIssueNumbers(commit)).containsOnly(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueNumberTitlePrefixAndSuffix() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title (#456)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.extractIssueNumbers(commit)).containsOnly(123, 456);
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueNumberTitleNotSeparatedBySpace() {
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix#123Commit title(#456)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(task.extractIssueNumbers(commit)).containsOnly(123, 456);
|
||||
}
|
||||
|
||||
static final VoidAnswer4<Issues, Set<Integer>, Set<String>, Map<String, String>> MOCK_FETCH_ISSUES = (ignore1,
|
||||
issues, ignore2, resolved) -> issues.forEach(i -> resolved.put("#" + i, "alternative title for " + i));
|
||||
|
||||
static final Issues MOCK_ISSUES = Mockito.mock(Issues.class);
|
||||
|
||||
@Test
|
||||
void generateChangelogDescriptionSingleIssueTwice() throws IOException {
|
||||
final GenerateReleaseNotesTask spy = Mockito.spy(task);
|
||||
doAnswer(answerVoid(MOCK_FETCH_ISSUES)).when(spy).fetchIssueLabelsAndTitles(any(), any(), any(), any());
|
||||
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title (#123)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(spy.parseChangeLogEntry(githubClient.randomRepo().issues(), commit).description)
|
||||
.isEqualTo("Commit title (#123)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateChangelogDescriptionTwoIssues() throws IOException {
|
||||
final GenerateReleaseNotesTask spy = Mockito.spy(task);
|
||||
doAnswer(answerVoid(MOCK_FETCH_ISSUES)).when(spy).fetchIssueLabelsAndTitles(any(), any(), any(), any());
|
||||
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title (#456)", "fullMessage",
|
||||
"Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(spy.parseChangeLogEntry(MOCK_ISSUES, commit).description).isEqualTo("Commit title (#123, #456)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateChangelogDescriptionTwoIssuesNoSpace() throws IOException {
|
||||
final GenerateReleaseNotesTask spy = Mockito.spy(task);
|
||||
doAnswer(answerVoid(MOCK_FETCH_ISSUES)).when(spy).fetchIssueLabelsAndTitles(any(), any(), any(), any());
|
||||
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123Commit title no space(#456)",
|
||||
"fullMessage", "Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(spy.parseChangeLogEntry(githubClient.randomRepo().issues(), commit).description)
|
||||
.isEqualTo("Commit title no space (#123, #456)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateChangelogDescriptionNoMatchingIssueUsesShortSha1() throws IOException {
|
||||
final GenerateReleaseNotesTask spy = Mockito.spy(task);
|
||||
doNothing().when(spy).fetchIssueLabelsAndTitles(any(), any(), any(), any());
|
||||
|
||||
SimpleCommit commit = new SimpleCommit("sha1", "fullsha1", "prefix #123 Commit title no space(#456)",
|
||||
"fullMessage", "Simon Baslé", "sbasle@pivotal.io", "Simon Baslé", "sbasle@pivotal.io", false);
|
||||
|
||||
assertThat(spy.parseChangeLogEntry(githubClient.randomRepo().issues(), commit).description)
|
||||
.isEqualTo("Commit title no space (sha1)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void issueInfoFetchProtectedWhenIssueNotFound() throws IOException {
|
||||
Issues issuesClient = githubClient.randomRepo().issues();
|
||||
Set<String> labels = new HashSet<>();
|
||||
Map<String, String> associatedIssues = new HashMap<>();
|
||||
|
||||
assertThatExceptionOfType(Exception.class).as("github client fails")
|
||||
.isThrownBy(() -> new Issue.Smart(issuesClient.get(123)).title());
|
||||
|
||||
assertThatCode(() -> task.fetchIssueLabelsAndTitles(issuesClient, Collections.singleton(123), labels,
|
||||
associatedIssues)).as("fetching just does nothing").doesNotThrowAnyException();
|
||||
|
||||
assertThat(labels).as("labels").isEmpty();
|
||||
assertThat(associatedIssues).as("associated issues").isEmpty();
|
||||
}
|
||||
|
||||
// TODO test login extraction by mocking the task's commitToGithubMention method
|
||||
|
||||
// TODO find a way to mock commits and thus test extractContributors
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 releaser.reactor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.mock.MkGithub;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mockito;
|
||||
import releaser.internal.Releaser;
|
||||
import releaser.internal.git.ProjectGitHandler;
|
||||
import releaser.internal.options.Parser;
|
||||
import releaser.internal.spring.ExecutionResultHandler;
|
||||
import releaser.internal.spring.SpringReleaser;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
/**
|
||||
* A configuration for tests, that mocks external clients but reuses the base bean
|
||||
* declaration for tasks.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("test")
|
||||
public class ReactorTestConfiguration {
|
||||
|
||||
@Bean
|
||||
RestartSiteProjectPostReleaseTask restartSiteProjectPostReleaseTask(Releaser releaser, CfClient cfClient,
|
||||
@Value("${cf.reactorAppName}") String reactorAppName) {
|
||||
return new RestartSiteProjectPostReleaseTask(releaser, cfClient, reactorAppName);
|
||||
}
|
||||
|
||||
@Bean
|
||||
GenerateReleaseNotesTask releaseNotesTask(Github github, ProjectGitHandler gitHandler) {
|
||||
return new GenerateReleaseNotesTask(github, gitHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ProjectGitHandler mockGitHandler() {
|
||||
return Mockito.mock(ProjectGitHandler.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
MkGithub mockGithub() {
|
||||
try {
|
||||
return new MkGithub();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new BeanCreationException("Unable to create mock Github bean", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
CfClient mockCfClient() {
|
||||
return BDDMockito.mock(CfClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
SpringReleaser mockReleaser() {
|
||||
return Mockito.mock(SpringReleaser.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
ExecutionResultHandler mockExecutionResultHandler() {
|
||||
return Mockito.mock(ExecutionResultHandler.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
Parser mockParser() {
|
||||
return Mockito.mock(Parser.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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 releaser.reactor;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.options.Options;
|
||||
import releaser.internal.project.ProjectVersion;
|
||||
import releaser.internal.project.Projects;
|
||||
import releaser.internal.spring.Arguments;
|
||||
import releaser.internal.spring.ProjectToRun;
|
||||
import releaser.internal.spring.ProjectsFromBom;
|
||||
import releaser.internal.tech.ExecutionResult;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("test")
|
||||
class RestartSiteProjectPostReleaseTaskTests {
|
||||
|
||||
@Autowired
|
||||
RestartSiteProjectPostReleaseTask task;
|
||||
|
||||
@Autowired
|
||||
CfClient cfClient;
|
||||
|
||||
@Test
|
||||
void should_update_the_website() {
|
||||
Arguments arguments = Arguments.forProject(reactorCoreProject());
|
||||
|
||||
ExecutionResult result = task.runTask(arguments);
|
||||
|
||||
BDDAssertions.then(result.isSuccess()).isTrue();
|
||||
BDDMockito.then(this.cfClient).should().restartApp(BDDMockito.eq("projectreactor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_fail_if_original_version_is_null() {
|
||||
ProjectToRun p = new ProjectToRun(null, new ProjectsFromBom(new Projects(), new ProjectVersion("foo", "1.0.0")),
|
||||
null, new ReleaserProperties(), BDDMockito.mock(Options.class)) {
|
||||
@Override
|
||||
public String name() {
|
||||
return "reactor-core";
|
||||
}
|
||||
};
|
||||
try {
|
||||
Arguments.forProject(p);
|
||||
fail();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_the_website_if_project_not_reactor_core() {
|
||||
Arguments arguments = Arguments.forProject(nonReactorCoreProject());
|
||||
|
||||
ExecutionResult result = task.runTask(arguments);
|
||||
|
||||
BDDAssertions.then(result.isSuccess()).isTrue();
|
||||
BDDMockito.then(this.cfClient).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
private ProjectToRun reactorCoreProject() {
|
||||
return new ProjectToRun(null, new ProjectsFromBom(new Projects(), new ProjectVersion("foo", "1.0.0")),
|
||||
new ProjectVersion("foo", "1.0.0"), new ReleaserProperties(), BDDMockito.mock(Options.class)) {
|
||||
@Override
|
||||
public String name() {
|
||||
return "reactor-core";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ProjectToRun nonReactorCoreProject() {
|
||||
return new ProjectToRun(null, new ProjectsFromBom(new Projects(), new ProjectVersion("foo", "1.0.0")),
|
||||
new ProjectVersion("foo", "1.0.0"), new ReleaserProperties(), BDDMockito.mock(Options.class)) {
|
||||
@Override
|
||||
public String name() {
|
||||
return "whatever";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
3
projects/reactor/src/test/resources/application-test.yml
Normal file
3
projects/reactor/src/test/resources/application-test.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
cf:
|
||||
username: foo
|
||||
password: bar
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-projects</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -138,30 +138,30 @@ class SpringCloudStreamMavenBomParser implements CustomBomParser {
|
||||
public Set<Project> setVersion(Set<Project> projects, String projectName, String version) {
|
||||
Set<Project> newProjects = new LinkedHashSet<>(projects);
|
||||
switch (projectName) {
|
||||
case SPRING_BOOT:
|
||||
case BOOT_STARTER_ARTIFACT_ID:
|
||||
case BOOT_STARTER_PARENT_ARTIFACT_ID:
|
||||
case BOOT_DEPENDENCIES_ARTIFACT_ID:
|
||||
updateBootVersions(newProjects, version);
|
||||
break;
|
||||
case BUILD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_PARENT_ARTIFACT_ID:
|
||||
updateBuildVersions(newProjects, version);
|
||||
break;
|
||||
case CLOUD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_ARTIFACT_ID:
|
||||
case CLOUD_RELEASE_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_PARENT_ARTIFACT_ID:
|
||||
updateSpringCloudVersions(newProjects, version);
|
||||
break;
|
||||
case STREAM_DEPS_ARTIFACT_ID:
|
||||
case STREAM_STARTER_ARTIFACT_ID:
|
||||
case STREAM_STARTER_BUILD_ARTIFACT_ID:
|
||||
case STREAM_STARTER_PARENT_ARTIFACT_ID:
|
||||
case STREAM_DOCS_ARTIFACT_ID:
|
||||
updateStreamVersions(newProjects, version);
|
||||
break;
|
||||
case SPRING_BOOT:
|
||||
case BOOT_STARTER_ARTIFACT_ID:
|
||||
case BOOT_STARTER_PARENT_ARTIFACT_ID:
|
||||
case BOOT_DEPENDENCIES_ARTIFACT_ID:
|
||||
updateBootVersions(newProjects, version);
|
||||
break;
|
||||
case BUILD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_PARENT_ARTIFACT_ID:
|
||||
updateBuildVersions(newProjects, version);
|
||||
break;
|
||||
case CLOUD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_ARTIFACT_ID:
|
||||
case CLOUD_RELEASE_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_PARENT_ARTIFACT_ID:
|
||||
updateSpringCloudVersions(newProjects, version);
|
||||
break;
|
||||
case STREAM_DEPS_ARTIFACT_ID:
|
||||
case STREAM_STARTER_ARTIFACT_ID:
|
||||
case STREAM_STARTER_BUILD_ARTIFACT_ID:
|
||||
case STREAM_STARTER_PARENT_ARTIFACT_ID:
|
||||
case STREAM_DOCS_ARTIFACT_ID:
|
||||
updateStreamVersions(newProjects, version);
|
||||
break;
|
||||
}
|
||||
return newProjects;
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-projects</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -139,31 +139,31 @@ class SpringCloudMavenBomParser implements CustomBomParser {
|
||||
public Set<Project> setVersion(Set<Project> projects, String projectName, String version) {
|
||||
Set<Project> newProjects = new LinkedHashSet<>(projects);
|
||||
switch (projectName) {
|
||||
case SPRING_BOOT:
|
||||
case BOOT_STARTER_ARTIFACT_ID:
|
||||
case BOOT_STARTER_PARENT_ARTIFACT_ID:
|
||||
case BOOT_DEPENDENCIES_ARTIFACT_ID:
|
||||
updateBootVersions(newProjects, version);
|
||||
break;
|
||||
case BUILD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_PARENT_ARTIFACT_ID:
|
||||
updateBuildVersions(newProjects, version);
|
||||
break;
|
||||
case CLOUD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_ARTIFACT_ID:
|
||||
case CLOUD_RELEASE_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_PARENT_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_BUILD_ARTIFACT_ID:
|
||||
updateSpringCloudVersions(newProjects, version);
|
||||
break;
|
||||
case STREAM_DEPS_ARTIFACT_ID:
|
||||
case STREAM_STARTER_ARTIFACT_ID:
|
||||
case STREAM_STARTER_BUILD_ARTIFACT_ID:
|
||||
case STREAM_STARTER_PARENT_ARTIFACT_ID:
|
||||
case STREAM_DOCS_ARTIFACT_ID:
|
||||
updateStreamVersions(newProjects, version);
|
||||
break;
|
||||
case SPRING_BOOT:
|
||||
case BOOT_STARTER_ARTIFACT_ID:
|
||||
case BOOT_STARTER_PARENT_ARTIFACT_ID:
|
||||
case BOOT_DEPENDENCIES_ARTIFACT_ID:
|
||||
updateBootVersions(newProjects, version);
|
||||
break;
|
||||
case BUILD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_PARENT_ARTIFACT_ID:
|
||||
updateBuildVersions(newProjects, version);
|
||||
break;
|
||||
case CLOUD_ARTIFACT_ID:
|
||||
case CLOUD_DEPENDENCIES_ARTIFACT_ID:
|
||||
case CLOUD_RELEASE_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_PARENT_ARTIFACT_ID:
|
||||
case CLOUD_STARTER_BUILD_ARTIFACT_ID:
|
||||
updateSpringCloudVersions(newProjects, version);
|
||||
break;
|
||||
case STREAM_DEPS_ARTIFACT_ID:
|
||||
case STREAM_STARTER_ARTIFACT_ID:
|
||||
case STREAM_STARTER_BUILD_ARTIFACT_ID:
|
||||
case STREAM_STARTER_PARENT_ARTIFACT_ID:
|
||||
case STREAM_DOCS_ARTIFACT_ID:
|
||||
updateStreamVersions(newProjects, version);
|
||||
break;
|
||||
}
|
||||
return newProjects;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package releaser.cloud.github;
|
||||
|
||||
import org.kohsuke.github.GitHub;
|
||||
import com.jcabi.github.Github;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.github.CustomGithubIssues;
|
||||
import releaser.internal.github.GithubIssueFiler;
|
||||
@@ -38,41 +38,25 @@ class SpringCloudGithubIssues implements CustomGithubIssues {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
SpringCloudGithubIssues(GitHub github, ReleaserProperties properties) {
|
||||
SpringCloudGithubIssues(Github github, ReleaserProperties properties) {
|
||||
this.githubIssueFiler = new GithubIssueFiler(github, properties);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileIssueInSpringGuides(Projects projects, ProjectVersion version) {
|
||||
String user = getGuidesOrg();
|
||||
String repo = getGuidesRepo();
|
||||
String user = "spring-guides";
|
||||
String repo = "getting-started-guides";
|
||||
this.githubIssueFiler.fileAGitHubIssue(user, repo, version, issueTitle(), guidesIssueText(projects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileIssueInStartSpringIo(Projects projects, ProjectVersion version) {
|
||||
String user = getStartSpringIoOrg();
|
||||
String repo = getStartSpringIoRepo();
|
||||
String user = "spring-io";
|
||||
String repo = "start.spring.io";
|
||||
this.githubIssueFiler.fileAGitHubIssue(user, repo, version, issueTitle(), startSpringIoIssueText(projects));
|
||||
}
|
||||
|
||||
String getGuidesOrg() {
|
||||
return "spring-guides";
|
||||
}
|
||||
|
||||
String getGuidesRepo() {
|
||||
return "getting-started-guides";
|
||||
}
|
||||
|
||||
String getStartSpringIoOrg() {
|
||||
return "spring-io";
|
||||
}
|
||||
|
||||
String getStartSpringIoRepo() {
|
||||
return "start.spring.io";
|
||||
}
|
||||
|
||||
private String issueTitle() {
|
||||
return String.format(GITHUB_ISSUE_TITLE, StringUtils.capitalize(parsedVersion()));
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ releaser:
|
||||
update-release-train-wiki: true
|
||||
update-all-test-samples: true
|
||||
all-test-sample-urls:
|
||||
spring-cloud-sleuth:
|
||||
- https://github.com/spring-cloud-samples/sleuth-issues
|
||||
- https://github.com/spring-cloud-samples/sleuth-documentation-apps
|
||||
- https://github.com/spring-cloud-samples/spring-cloud-sleuth-samples
|
||||
spring-cloud-contract:
|
||||
- https://github.com/spring-cloud-samples/spring-cloud-contract-samples
|
||||
- https://github.com/spring-cloud-samples/the-legacy-app
|
||||
@@ -97,7 +101,7 @@ releaser:
|
||||
enabled: true
|
||||
template-folder: cloud
|
||||
versions:
|
||||
all-versions-file-url: https://raw.githubusercontent.com/spring-io/start.spring.io/main/start-site/src/main/resources/application.yml
|
||||
all-versions-file-url: https://raw.githubusercontent.com/spring-io/start.spring.io/master/start-site/src/main/resources/application.yml
|
||||
bom-name: spring-cloud
|
||||
# fixed-versions:
|
||||
meta-release:
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -43,7 +45,7 @@ import org.springframework.util.FileSystemUtils;
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
public class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
|
||||
File project;
|
||||
|
||||
@@ -59,7 +61,7 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
ReleaserProperties properties = SpringCloudReleaserProperties.get();
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException, URISyntaxException {
|
||||
public void setup() throws IOException, URISyntaxException {
|
||||
this.project = new File(SpringCloudCustomProjectDocumentationUpdater.class
|
||||
.getResource("/projects/spring-cloud-static").toURI());
|
||||
TestUtils.prepareLocalRepo();
|
||||
@@ -71,17 +73,19 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
Collections.singletonList(SpringCloudGithubIssuesAccessor.springCloud(this.properties)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DocumentationUpdater projectDocumentationUpdater(ReleaserProperties properties) {
|
||||
return new DocumentationUpdater(this.handler, properties, templateGenerator(properties),
|
||||
Collections.singletonList(new SpringCloudCustomProjectDocumentationUpdater(this.handler, properties)));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TemplateGenerator templateGenerator(ReleaserProperties properties) {
|
||||
return new TemplateGenerator(properties, this.gitHubHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_current_version_in_the_docs_if_current_release_starts_with_v_and_then_lower_letter_than_the_stored_release()
|
||||
public void should_not_update_current_version_in_the_docs_if_current_release_starts_with_v_and_then_lower_letter_than_the_stored_release()
|
||||
throws URISyntaxException, IOException {
|
||||
ProjectVersion releaseTrainVersion = new ProjectVersion("spring-cloud-release", "Finchley.SR33");
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
@@ -110,7 +114,7 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_commit_if_the_same_version_is_already_there() {
|
||||
public void should_not_commit_if_the_same_version_is_already_there() {
|
||||
ProjectVersion releaseTrainVersion = new ProjectVersion("spring-cloud-release", "Dalston.SR3");
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setDocumentationUrl(this.clonedDocProject.toURI().toString());
|
||||
@@ -123,7 +127,7 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_when_release_train_docs_update_happen_for_a_project_that_does_not_start_with_spring_cloud() {
|
||||
public void should_do_nothing_when_release_train_docs_update_happen_for_a_project_that_does_not_start_with_spring_cloud() {
|
||||
ProjectVersion springBootVersion = new ProjectVersion("spring-boot", "2.2.5");
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setDocumentationUrl(this.clonedDocProject.toURI().toString());
|
||||
@@ -136,7 +140,7 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_do_nothing_when_single_project_docs_update_happen_for_a_project_that_does_not_start_with_spring_cloud() {
|
||||
public void should_do_nothing_when_single_project_docs_update_happen_for_a_project_that_does_not_start_with_spring_cloud() {
|
||||
ProjectVersion springBootVersion = new ProjectVersion("spring-boot", "2.2.5");
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setDocumentationUrl(this.clonedDocProject.toURI().toString());
|
||||
@@ -149,7 +153,7 @@ class SpringCloudCustomProjectDocumentationUpdaterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_current_version_in_the_docs_if_current_release_starts_with_lower_letter_than_the_stored_release()
|
||||
public void should_not_update_current_version_in_the_docs_if_current_release_starts_with_lower_letter_than_the_stored_release()
|
||||
throws IOException {
|
||||
ProjectVersion releaseTrainVersion = new ProjectVersion("spring-cloud-release", "Angel.SR33");
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
package releaser.cloud.github;
|
||||
|
||||
import org.kohsuke.github.GitHub;
|
||||
import com.jcabi.github.Github;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.github.CustomGithubIssues;
|
||||
|
||||
public class SpringCloudGithubIssuesAccessor {
|
||||
|
||||
public static CustomGithubIssues springCloud(GitHub github, ReleaserProperties releaserProperties) {
|
||||
public static CustomGithubIssues springCloud(Github github, ReleaserProperties releaserProperties) {
|
||||
return new SpringCloudGithubIssues(github, releaserProperties);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2013-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 releaser.cloud.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Issue;
|
||||
import com.jcabi.github.Repo;
|
||||
import com.jcabi.github.Repos;
|
||||
import com.jcabi.github.mock.MkGithub;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
import releaser.cloud.SpringCloudReleaserProperties;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.github.CustomGithubIssues;
|
||||
import releaser.internal.project.ProjectVersion;
|
||||
import releaser.internal.project.Projects;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class SpringCloudGithubIssuesTests {
|
||||
|
||||
ReleaserProperties properties = SpringCloudReleaserProperties.get();
|
||||
|
||||
MkGithub github;
|
||||
|
||||
Repo repo;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws IOException {
|
||||
this.github = github("spring-guides");
|
||||
this.properties.getGit().setOauthToken("a");
|
||||
this.repo = createGettingStartedGuides(this.github);
|
||||
}
|
||||
|
||||
public void setupStartSpringIo() throws IOException {
|
||||
this.github = github("spring-io");
|
||||
this.repo = createStartSpringIo(this.github);
|
||||
}
|
||||
|
||||
private MkGithub github(String login) throws IOException {
|
||||
return new MkGithub(login);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_do_anything_for_non_release_train_version() {
|
||||
Github github = BDDMockito.mock(Github.class);
|
||||
CustomGithubIssues githubIssues = new SpringCloudGithubIssues(github, properties);
|
||||
|
||||
githubIssues.fileIssueInSpringGuides(
|
||||
new Projects(new ProjectVersion("foo", "1.0.0.BUILD-SNAPSHOT"),
|
||||
new ProjectVersion("spring-cloud-build", "2.0.0.BUILD-SNAPSHOT")),
|
||||
new ProjectVersion("sc-release", "Edgware.BUILD-SNAPSHOT"));
|
||||
|
||||
BDDMockito.then(github).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_file_an_issue_for_release_version() throws IOException {
|
||||
CustomGithubIssues issues = new SpringCloudGithubIssues(github, properties);
|
||||
properties.getPom().setBranch("vEdgware.RELEASE");
|
||||
|
||||
issues.fileIssueInSpringGuides(
|
||||
new Projects(new ProjectVersion("spring-cloud-foo", "1.0.0.RELEASE"),
|
||||
new ProjectVersion("spring-cloud-build", "2.0.0.RELEASE"),
|
||||
new ProjectVersion("bar", "2.0.0.RELEASE"), new ProjectVersion("baz", "3.0.0.RELEASE")),
|
||||
new ProjectVersion("sc-release", "Edgware.RELEASE"));
|
||||
|
||||
Issue issue = this.github.repos().get(new Coordinates.Simple("spring-guides", "getting-started-guides"))
|
||||
.issues().get(1);
|
||||
then(issue.exists()).isTrue();
|
||||
Issue.Smart smartIssue = new Issue.Smart(issue);
|
||||
then(smartIssue.title()).isEqualTo("Upgrade to Spring Cloud Edgware.RELEASE");
|
||||
then(smartIssue.body()).contains(
|
||||
"Release train [spring-cloud-release] in version [Edgware.RELEASE] released with the following projects")
|
||||
.contains("spring-cloud-foo : `1.0.0.RELEASE`").contains("bar : `2.0.0.RELEASE`")
|
||||
.contains("baz : `3.0.0.RELEASE`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_when_no_token_was_passed() {
|
||||
properties.getGit().setOauthToken("");
|
||||
CustomGithubIssues issues = new SpringCloudGithubIssues(github, properties);
|
||||
|
||||
thenThrownBy(() -> issues.fileIssueInSpringGuides(
|
||||
new Projects(Collections.singletonList(new ProjectVersion("spring-cloud-build", "2.0.0.RELEASE"))),
|
||||
nonGaSleuthProject())).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(
|
||||
"You have to pass Github OAuth token for milestone closing to be operational");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_do_anything_for_non_release_train_version_when_updating_startspringio() throws IOException {
|
||||
setupStartSpringIo();
|
||||
Github github = BDDMockito.mock(Github.class);
|
||||
CustomGithubIssues issues = new SpringCloudGithubIssues(github, properties);
|
||||
|
||||
issues.fileIssueInStartSpringIo(
|
||||
new Projects(new ProjectVersion("foo", "1.0.0.BUILD-SNAPSHOT"),
|
||||
new ProjectVersion("spring-cloud-build", "2.0.0.RELEASE")),
|
||||
new ProjectVersion("sc-release", "Edgware.BUILD-SNAPSHOT"));
|
||||
|
||||
BDDMockito.then(github).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_file_an_issue_for_release_version_when_updating_startspringio() throws IOException {
|
||||
setupStartSpringIo();
|
||||
CustomGithubIssues issues = new SpringCloudGithubIssues(github, properties);
|
||||
properties.getPom().setBranch("vEdgware.RELEASE");
|
||||
|
||||
issues.fileIssueInStartSpringIo(new Projects(new ProjectVersion("spring-cloud-foo", "1.0.0.RELEASE"),
|
||||
new ProjectVersion("spring-cloud-build", "2.0.0.RELEASE"), new ProjectVersion("bar", "2.0.0.RELEASE"),
|
||||
new ProjectVersion("baz", "3.0.0.RELEASE"), new ProjectVersion("spring-boot", "1.2.3.RELEASE")),
|
||||
new ProjectVersion("sc-release", "Edgware.RELEASE"));
|
||||
|
||||
Issue issue = this.github.repos().get(new Coordinates.Simple("spring-io", "start.spring.io")).issues().get(1);
|
||||
then(issue.exists()).isTrue();
|
||||
Issue.Smart smartIssue = new Issue.Smart(issue);
|
||||
then(smartIssue.title()).isEqualTo("Upgrade to Spring Cloud Edgware.RELEASE");
|
||||
then(smartIssue.body()).contains(
|
||||
"Release train [spring-cloud-release] in version [Edgware.RELEASE] released with the Spring Boot version [`1.2.3.RELEASE`]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_exception_when_no_token_was_passed_when_updating_startspringio() throws IOException {
|
||||
setupStartSpringIo();
|
||||
properties.getGit().setOauthToken("");
|
||||
CustomGithubIssues issues = new SpringCloudGithubIssues(github, properties);
|
||||
|
||||
thenThrownBy(() -> issues.fileIssueInStartSpringIo(
|
||||
new Projects(Collections.singletonList(new ProjectVersion("spring-cloud-build", "2.0.0.RELEASE"))),
|
||||
nonGaSleuthProject())).isInstanceOf(IllegalArgumentException.class).hasMessageContaining(
|
||||
"You have to pass Github OAuth token for milestone closing to be operational");
|
||||
}
|
||||
|
||||
private Repo createGettingStartedGuides(MkGithub github) throws IOException {
|
||||
return github.repos().create(new Repos.RepoCreate("getting-started-guides", false));
|
||||
}
|
||||
|
||||
private Repo createStartSpringIo(MkGithub github) throws IOException {
|
||||
return github.repos().create(new Repos.RepoCreate("start.spring.io", false));
|
||||
}
|
||||
|
||||
private ProjectVersion nonGaSleuthProject() {
|
||||
return new ProjectVersion("spring-cloud-sleuth", "0.2.0.BUILD-SNAPSHOT");
|
||||
}
|
||||
|
||||
ReleaserProperties withToken() {
|
||||
ReleaserProperties properties = SpringCloudReleaserProperties.get();
|
||||
properties.getGit().setOauthToken("foo");
|
||||
properties.getPom().setBranch("vEdgware.RELEASE");
|
||||
properties.getGit().setUpdateSpringGuides(true);
|
||||
properties.getGit().setUpdateStartSpringIo(true);
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user