Revert "Spring Boot 3 migration (#255)"
This reverts commit aa5d636de3.
This commit is contained in:
2
.github/workflows/maven.yml
vendored
2
.github/workflows/maven.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["17"]
|
||||
java: ["8"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-parent</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>releaser-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -16,11 +16,11 @@
|
||||
<configprops.inclusionPattern>releaser.*</configprops.inclusionPattern>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!--<dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-info</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>-->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>releaser-core</artifactId>
|
||||
|
||||
123
pom.xml
123
pom.xml
@@ -6,13 +6,13 @@
|
||||
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<artifactId>releaser-parent</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<version>3.1.1</version>
|
||||
<relativePath/>
|
||||
<!-- lookup parent from repository -->
|
||||
</parent>
|
||||
@@ -28,17 +28,18 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
|
||||
<spring-cloud-bom.version>2022.0.3</spring-cloud-bom.version>
|
||||
<github-api.version>1.315</github-api.version>
|
||||
<spring-cloud-bom.version>2021.0.1</spring-cloud-bom.version>
|
||||
<jcabi-github.version>1.1.2</jcabi-github.version>
|
||||
<jsch-agent.version>0.0.9</jsch-agent.version>
|
||||
<hibernate-validator.version>7.0.4.Final</hibernate-validator.version>
|
||||
<!--Matches JGit in Boot:-->
|
||||
<org.eclipse.jgit-version>6.5.0.202303070854-r</org.eclipse.jgit-version>
|
||||
<org.eclipse.jgit-version>5.12.0.202106070339-r</org.eclipse.jgit-version>
|
||||
<!-- Versions plugin uses this version -->
|
||||
<maven-model.version>3.8.5</maven-model.version>
|
||||
<versions-maven-plugin.version>2.10.0</versions-maven-plugin.version>
|
||||
<handlebars.version>4.3.1</handlebars.version>
|
||||
<handlebars.version>4.3.0</handlebars.version>
|
||||
<initializr-metadata.version>0.12.0</initializr-metadata.version>
|
||||
<awaitility.version>4.2.0</awaitility.version>
|
||||
<sagan-site.version>1.0.0-SNAPSHOT</sagan-site.version>
|
||||
@@ -46,7 +47,6 @@
|
||||
<jopt-simple.version>5.0.4</jopt-simple.version>
|
||||
<fliptables.version>1.1.0</fliptables.version>
|
||||
<zt.exec.version>1.12</zt.exec.version>
|
||||
<okhttp.version>4.11.0</okhttp.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -58,93 +58,6 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>${org.eclipse.jgit-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
|
||||
<version>${org.eclipse.jgit-version}</version>
|
||||
</dependency>
|
||||
<!-- a proxy to ssh-agent and Pageant in Java -->
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.sshagent</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.jsch</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.usocket-jna</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model</artifactId>
|
||||
<!--<version>3.3.9</version>-->
|
||||
<!-- Versions plugin uses this version -->
|
||||
<version>${maven-model.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${versions-maven-plugin.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>github-api</artifactId>
|
||||
<version>${github-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttp.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jknack</groupId>
|
||||
<artifactId>handlebars</artifactId>
|
||||
<version>${handlebars.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.initializr</groupId>
|
||||
<artifactId>initializr-metadata</artifactId>
|
||||
<version>${initializr-metadata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
<version>${javax.json.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
<artifactId>zt-exec</artifactId>
|
||||
<version>${zt.exec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -190,12 +103,20 @@
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/release</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-snapshots</id>
|
||||
<name>Spring Snapshots</name>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
@@ -206,7 +127,15 @@
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring Milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>spring-releases</id>
|
||||
<name>Spring Releases</name>
|
||||
<url>https://repo.spring.io/libs-release-local</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<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>
|
||||
|
||||
@@ -35,31 +35,40 @@
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
|
||||
<version>${org.eclipse.jgit-version}</version>
|
||||
</dependency>
|
||||
<!-- a proxy to ssh-agent and Pageant in Java -->
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.sshagent</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit.ssh.jsch</artifactId>
|
||||
<version>${org.eclipse.jgit-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.jsch</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch.agentproxy.usocket-jna</artifactId>
|
||||
<version>${jsch-agent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model</artifactId>
|
||||
<!--<version>3.3.9</version>-->
|
||||
<!-- Versions plugin uses this version -->
|
||||
<version>${maven-model.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>versions-maven-plugin</artifactId>
|
||||
<version>${versions-maven-plugin.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@@ -76,20 +85,19 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kohsuke</groupId>
|
||||
<artifactId>github-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<groupId>com.jcabi</groupId>
|
||||
<artifactId>jcabi-github</artifactId>
|
||||
<version>${jcabi-github.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jknack</groupId>
|
||||
<artifactId>handlebars</artifactId>
|
||||
<version>${handlebars.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.spring.initializr</groupId>
|
||||
<artifactId>initializr-metadata</artifactId>
|
||||
<version>${initializr-metadata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -104,6 +112,7 @@
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>
|
||||
@@ -122,11 +131,13 @@
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
<version>${javax.json.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
<artifactId>zt-exec</artifactId>
|
||||
<version>${zt.exec.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package releaser.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -27,7 +26,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -428,12 +428,6 @@ public class ReleaserProperties implements Serializable {
|
||||
|
||||
public static class Git implements Serializable {
|
||||
|
||||
/**
|
||||
* Absolute path to a directory with cache for OkHTTP calls to GitHub.
|
||||
*/
|
||||
@NotBlank
|
||||
private String cacheDirectory = temporaryDirectory();
|
||||
|
||||
/**
|
||||
* URL to a release train repository.
|
||||
*/
|
||||
@@ -822,14 +816,6 @@ public class ReleaserProperties implements Serializable {
|
||||
this.orgName = orgName;
|
||||
}
|
||||
|
||||
public String getCacheDirectory() {
|
||||
return cacheDirectory;
|
||||
}
|
||||
|
||||
public void setCacheDirectory(String cacheDirectory) {
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Git{" + "releaseTrainBomUrl='" + this.releaseTrainBomUrl + '\'' + ", documentationUrl='"
|
||||
@@ -844,15 +830,6 @@ public class ReleaserProperties implements Serializable {
|
||||
+ this.updateSpringProject + ", sampleUrlsSize=" + this.allTestSampleUrls.size() + '}';
|
||||
}
|
||||
|
||||
private static String temporaryDirectory() {
|
||||
try {
|
||||
return Files.createTempDirectory("github-cache").toAbsolutePath().toString();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Pom implements Serializable {
|
||||
@@ -1217,7 +1194,7 @@ public class ReleaserProperties implements Serializable {
|
||||
* A mapping that should be applied to {@code gradle.properties} in order to
|
||||
* perform a substitution of properties. The mapping is from a property inside
|
||||
* {@code gradle.properties} to the projects name. Example.
|
||||
* <p>
|
||||
*
|
||||
* In {@code gradle.properties} you have {@code verifierVersion=1.0.0} . You want
|
||||
* this property to get updated with the value of {@code spring-cloud-contract}
|
||||
* version. Then it's enough to do the mapping like this for this Releaser's
|
||||
@@ -1389,7 +1366,7 @@ public class ReleaserProperties implements Serializable {
|
||||
/**
|
||||
* URL to the Sagan API.
|
||||
*/
|
||||
private String baseUrl = "https://api.spring.io";
|
||||
private String baseUrl = "https://spring.io";
|
||||
|
||||
/**
|
||||
* Folder with asciidoctor files for docs.
|
||||
|
||||
@@ -56,12 +56,12 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.JschConfigSessionFactory;
|
||||
import org.eclipse.jgit.transport.OpenSshConfig;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.transport.SshTransport;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||
import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory;
|
||||
import org.eclipse.jgit.transport.ssh.jsch.OpenSshConfig;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.eclipse.jgit.util.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -16,39 +16,365 @@
|
||||
|
||||
package releaser.internal.github;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.kohsuke.github.GitHubBuilder;
|
||||
import org.kohsuke.github.extras.okhttp3.OkHttpGitHubConnector;
|
||||
import javax.json.JsonObject;
|
||||
|
||||
final class CachingGithub {
|
||||
import com.jcabi.github.Assignees;
|
||||
import com.jcabi.github.Branches;
|
||||
import com.jcabi.github.Collaborators;
|
||||
import com.jcabi.github.Contents;
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.DeployKeys;
|
||||
import com.jcabi.github.Forks;
|
||||
import com.jcabi.github.Gists;
|
||||
import com.jcabi.github.Git;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Gitignores;
|
||||
import com.jcabi.github.Hooks;
|
||||
import com.jcabi.github.IssueEvents;
|
||||
import com.jcabi.github.Issues;
|
||||
import com.jcabi.github.Labels;
|
||||
import com.jcabi.github.Language;
|
||||
import com.jcabi.github.Limits;
|
||||
import com.jcabi.github.Markdown;
|
||||
import com.jcabi.github.Milestones;
|
||||
import com.jcabi.github.Notifications;
|
||||
import com.jcabi.github.Organizations;
|
||||
import com.jcabi.github.Pulls;
|
||||
import com.jcabi.github.Releases;
|
||||
import com.jcabi.github.Repo;
|
||||
import com.jcabi.github.RepoCommits;
|
||||
import com.jcabi.github.Repos;
|
||||
import com.jcabi.github.Search;
|
||||
import com.jcabi.github.Stars;
|
||||
import com.jcabi.github.Users;
|
||||
import com.jcabi.http.Request;
|
||||
|
||||
private CachingGithub() {
|
||||
throw new IllegalStateException("Can't instantiate this");
|
||||
class CachingGithub implements Github, Closeable {
|
||||
|
||||
private final AtomicReference<Repos> repos = new AtomicReference<>();
|
||||
|
||||
private final Github delegate;
|
||||
|
||||
CachingGithub(Github delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
static GitHub INSTANCE;
|
||||
|
||||
static GitHub getInstance(String oauthToken, String cacheDirectory) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = github(oauthToken, cacheDirectory);
|
||||
}
|
||||
return INSTANCE;
|
||||
@Override
|
||||
public Request entry() {
|
||||
return this.delegate.entry();
|
||||
}
|
||||
|
||||
private static GitHub github(String oauthToken, String cacheDirectory) {
|
||||
Cache cache = new Cache(new File(cacheDirectory), 10 * 1024 * 1024); // 10MB cache
|
||||
try {
|
||||
return new GitHubBuilder().withOAuthToken(oauthToken)
|
||||
.withConnector(new OkHttpGitHubConnector(new OkHttpClient.Builder().cache(cache).build())).build();
|
||||
@Override
|
||||
public Repos repos() {
|
||||
Repos repos = this.repos.get();
|
||||
if (repos == null) {
|
||||
this.repos.set(new CachingRepos(this.delegate.repos()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
return this.repos.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Gists gists() {
|
||||
return this.delegate.gists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Users users() {
|
||||
return this.delegate.users();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Organizations organizations() {
|
||||
return this.delegate.organizations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Markdown markdown() {
|
||||
return this.delegate.markdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Limits limits() {
|
||||
return this.delegate.limits();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Search search() {
|
||||
return this.delegate.search();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Gitignores gitignores() throws IOException {
|
||||
return this.delegate.gitignores();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject meta() throws IOException {
|
||||
return this.delegate.meta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject emojis() throws IOException {
|
||||
return this.delegate.emojis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this.delegate.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Repos repos = this.repos.get();
|
||||
if (repos instanceof Closeable) {
|
||||
((Closeable) repos).close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CachingRepos implements Repos, Closeable {
|
||||
|
||||
private static final Map<Object, CachingRepo> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private final Repos delegate;
|
||||
|
||||
CachingRepos(Repos delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Github github() {
|
||||
return this.delegate.github();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repo create(RepoCreate repoCreate) throws IOException {
|
||||
return this.delegate.create(repoCreate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Repo get(Coordinates coordinates) {
|
||||
return CACHE.computeIfAbsent(coordinates, o -> new CachingRepo(this.delegate.get(coordinates)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Coordinates coordinates) throws IOException {
|
||||
this.delegate.remove(coordinates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Repo> iterate(String s) {
|
||||
return this.delegate.iterate(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Coordinates coordinates) throws IOException {
|
||||
return delegate.exists(coordinates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this.delegate.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
CACHE.forEach((o, repo) -> repo.close());
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CachingRepo implements Repo, Closeable {
|
||||
|
||||
private static final Map<RepoKey, Object> CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private final Repo delegate;
|
||||
|
||||
CachingRepo(Repo delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Github github() {
|
||||
return this.delegate.github();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coordinates coordinates() {
|
||||
return (Coordinates) CACHE.computeIfAbsent(new RepoKey(this.delegate, "coordinates"),
|
||||
s -> this.delegate.coordinates());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Issues issues() {
|
||||
return (Issues) CACHE.computeIfAbsent(new RepoKey(this.delegate, "issues"), s -> this.delegate.issues());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Milestones milestones() {
|
||||
return (Milestones) CACHE.computeIfAbsent(new RepoKey(this.delegate, "milestones"),
|
||||
s -> this.delegate.milestones());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pulls pulls() {
|
||||
return (Pulls) CACHE.computeIfAbsent(new RepoKey(this.delegate, "pulls"), s -> this.delegate.pulls());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hooks hooks() {
|
||||
return (Hooks) CACHE.computeIfAbsent(new RepoKey(this.delegate, "hooks"), s -> this.delegate.hooks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IssueEvents issueEvents() {
|
||||
return (IssueEvents) CACHE.computeIfAbsent(new RepoKey(this.delegate, "issueEvents"),
|
||||
s -> this.delegate.issueEvents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Labels labels() {
|
||||
return (Labels) CACHE.computeIfAbsent(new RepoKey(this.delegate, "labels"), s -> this.delegate.labels());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Assignees assignees() {
|
||||
return (Assignees) CACHE.computeIfAbsent(new RepoKey(this.delegate, "assignees"),
|
||||
s -> this.delegate.assignees());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Releases releases() {
|
||||
return (Releases) CACHE.computeIfAbsent(new RepoKey(this.delegate, "releases"), s -> this.delegate.releases());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeployKeys keys() {
|
||||
return (DeployKeys) CACHE.computeIfAbsent(new RepoKey(this.delegate, "keys"), s -> this.delegate.keys());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forks forks() {
|
||||
return (Forks) CACHE.computeIfAbsent(new RepoKey(this.delegate, "forks"), s -> this.delegate.forks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepoCommits commits() {
|
||||
return (RepoCommits) CACHE.computeIfAbsent(new RepoKey(this.delegate, "repoCommits"),
|
||||
s -> this.delegate.commits());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Branches branches() {
|
||||
return (Branches) CACHE.computeIfAbsent(new RepoKey(this.delegate, "branches"), s -> this.delegate.branches());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contents contents() {
|
||||
return (Contents) CACHE.computeIfAbsent(new RepoKey(this.delegate, "contents"), s -> this.delegate.contents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collaborators collaborators() {
|
||||
return (Collaborators) CACHE.computeIfAbsent(new RepoKey(this.delegate, "collaborators"),
|
||||
s -> this.delegate.collaborators());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Git git() {
|
||||
return (Git) CACHE.computeIfAbsent(new RepoKey(this.delegate, "git"), s -> this.delegate.git());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stars stars() {
|
||||
return (Stars) CACHE.computeIfAbsent(new RepoKey(this.delegate, "stars"), s -> this.delegate.stars());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Notifications notifications() {
|
||||
return (Notifications) CACHE.computeIfAbsent(new RepoKey(this.delegate, "notifications"),
|
||||
s -> this.delegate.notifications());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Language> languages() throws IOException {
|
||||
return this.delegate.languages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject json() throws IOException {
|
||||
return (JsonObject) CACHE.computeIfAbsent(new RepoKey(this.delegate, "json"), s -> {
|
||||
try {
|
||||
return this.delegate.json();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void patch(JsonObject jsonObject) throws IOException {
|
||||
this.delegate.patch(jsonObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Repo o) {
|
||||
return this.delegate.compareTo(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RepoKey {
|
||||
|
||||
final Repo repo;
|
||||
|
||||
final String key;
|
||||
|
||||
RepoKey(Repo repo, String key) {
|
||||
this.repo = repo;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
RepoKey repoKey = (RepoKey) o;
|
||||
return Objects.equals(this.repo, repoKey.repo) && Objects.equals(this.key, repoKey.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.repo, this.key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@ package releaser.internal.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.kohsuke.github.GHIssue;
|
||||
import org.kohsuke.github.GHIssueState;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Issue;
|
||||
import com.jcabi.github.Repo;
|
||||
import com.jcabi.github.RtGithub;
|
||||
import com.jcabi.http.wire.RetryWire;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
@@ -39,19 +40,19 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class GithubIssueFiler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GithubIssueFiler.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(GithubIssues.class);
|
||||
|
||||
private final GitHub github;
|
||||
private final Github github;
|
||||
|
||||
private final ReleaserProperties properties;
|
||||
|
||||
public GithubIssueFiler(ReleaserProperties properties) {
|
||||
this(CachingGithub.getInstance(properties.getGit().getOauthToken(), properties.getGit().getCacheDirectory()),
|
||||
this(new RtGithub(new RtGithub(properties.getGit().getOauthToken()).entry().through(RetryWire.class)),
|
||||
properties);
|
||||
}
|
||||
|
||||
public GithubIssueFiler(GitHub github, ReleaserProperties properties) {
|
||||
this.github = github;
|
||||
public GithubIssueFiler(Github github, ReleaserProperties properties) {
|
||||
this.github = new CachingGithub(github);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@@ -69,16 +70,15 @@ public class GithubIssueFiler {
|
||||
}
|
||||
|
||||
private void fileAGithubIssue(String user, String repo, String issueTitle, String issueText) {
|
||||
Repo ghRepo = this.github.repos().get(new Coordinates.Simple(user, repo));
|
||||
// check if the issue is not already there
|
||||
boolean issueAlreadyFiled = issueAlreadyFiled(ghRepo, issueTitle);
|
||||
if (issueAlreadyFiled) {
|
||||
log.info("Issue already filed, will not do that again");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
GHRepository ghRepo = github.getRepository(user + "/" + repo);
|
||||
// check if the issue is not already there
|
||||
boolean issueAlreadyFiled = issueAlreadyFiled(ghRepo, issueTitle);
|
||||
if (issueAlreadyFiled) {
|
||||
log.info("Issue already filed, will not do that again");
|
||||
return;
|
||||
}
|
||||
GHIssue ghIssue = ghRepo.createIssue(issueTitle).body(issueText).create();
|
||||
int number = ghIssue.getNumber();
|
||||
int number = ghRepo.issues().create(issueTitle, issueText).number();
|
||||
log.info("Successfully created an issue with " + "title [{}] for the [{}/{}] GitHub repository" + number,
|
||||
issueTitle, user, repo);
|
||||
}
|
||||
@@ -95,20 +95,23 @@ public class GithubIssueFiler {
|
||||
return version;
|
||||
}
|
||||
|
||||
boolean issueAlreadyFiled(GHRepository springGuides, String issueTitle) throws IOException {
|
||||
private boolean issueAlreadyFiled(Repo springGuides, String issueTitle) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("state", "open");
|
||||
int counter = 0;
|
||||
int maxIssues = 10;
|
||||
|
||||
for (Iterator<GHIssue> it = springGuides.getIssues(GHIssueState.OPEN).iterator(); it.hasNext();) {
|
||||
GHIssue issue = it.next();
|
||||
for (Issue issue : springGuides.issues().iterate(map)) {
|
||||
if (counter >= maxIssues) {
|
||||
return false;
|
||||
}
|
||||
String title = issue.getTitle();
|
||||
if (issueTitle.equals(title)) {
|
||||
return true;
|
||||
Issue.Smart smartIssue = new Issue.Smart(issue);
|
||||
try {
|
||||
if (issueTitle.equals(smartIssue.title())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,16 @@ package releaser.internal.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.kohsuke.github.GHIssueState;
|
||||
import org.kohsuke.github.GHMilestone;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Milestone;
|
||||
import com.jcabi.github.RtGithub;
|
||||
import com.jcabi.http.wire.RetryWire;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
@@ -38,21 +42,21 @@ import org.springframework.util.StringUtils;
|
||||
class GithubMilestones {
|
||||
|
||||
static final Map<ProjectVersion, String> MILESTONE_URL_CACHE = new ConcurrentHashMap<>();
|
||||
static final Map<ProjectVersion, GHMilestone> MILESTONE_CACHE = new ConcurrentHashMap<>();
|
||||
static final Map<ProjectVersion, Milestone.Smart> MILESTONE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GithubMilestones.class);
|
||||
|
||||
private final GitHub github;
|
||||
private final Github github;
|
||||
|
||||
private final ReleaserProperties properties;
|
||||
|
||||
GithubMilestones(ReleaserProperties properties) {
|
||||
this(CachingGithub.getInstance(properties.getGit().getOauthToken(), properties.getGit().getCacheDirectory()),
|
||||
this(new RtGithub(new RtGithub(properties.getGit().getOauthToken()).entry().through(RetryWire.class)),
|
||||
properties);
|
||||
}
|
||||
|
||||
GithubMilestones(GitHub github, ReleaserProperties properties) {
|
||||
this.github = github;
|
||||
GithubMilestones(Github github, ReleaserProperties properties) {
|
||||
this.github = new CachingGithub(github);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@@ -62,7 +66,7 @@ class GithubMilestones {
|
||||
+ "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=...]");
|
||||
GHMilestone foundMilestone = MILESTONE_CACHE.get(version);
|
||||
Milestone.Smart foundMilestone = MILESTONE_CACHE.get(version);
|
||||
String tagVersion = version.version;
|
||||
if (foundMilestone == null) {
|
||||
foundMilestone = matchingMilestone(tagVersion, openMilestones(version));
|
||||
@@ -85,26 +89,27 @@ class GithubMilestones {
|
||||
}
|
||||
}
|
||||
|
||||
GHMilestone matchingMilestone(String tagVersion, Iterable<GHMilestone> milestones) {
|
||||
Milestone.Smart matchingMilestone(String tagVersion, Iterable<Milestone> milestones) {
|
||||
log.debug("Successfully received list of milestones [{}]", milestones);
|
||||
log.info("Will try to match against tag version [{}]", tagVersion);
|
||||
try {
|
||||
int counter = 0;
|
||||
for (GHMilestone milestone : milestones) {
|
||||
for (Milestone milestone : milestones) {
|
||||
if (counter++ >= this.properties.getGit().getNumberOfCheckedMilestones()) {
|
||||
log.warn(
|
||||
"No matching milestones were found within the provided threshold [{}] of checked milestones",
|
||||
this.properties.getGit().getNumberOfCheckedMilestones());
|
||||
return null;
|
||||
}
|
||||
String title = milestone.getTitle();
|
||||
Milestone.Smart smartMilestone = new Milestone.Smart(milestone);
|
||||
String title = milestoneTitle(smartMilestone);
|
||||
if (tagVersion.equals(title) || numericVersion(tagVersion).equals(title)) {
|
||||
log.info("Found a matching milestone [{}]", milestone.getNumber());
|
||||
return milestone;
|
||||
log.info("Found a matching milestone [{}]", smartMilestone.number());
|
||||
return smartMilestone;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (AssertionError | IOException e) {
|
||||
log.error("Exception occurred while trying to retrieve the milestone", e);
|
||||
return null;
|
||||
}
|
||||
@@ -120,7 +125,7 @@ class GithubMilestones {
|
||||
Assert.hasText(this.properties.getGit().getOauthToken(),
|
||||
"You have to pass Github OAuth token for milestone closing to be operational");
|
||||
String tagVersion = version.version;
|
||||
GHMilestone foundMilestone = matchingMilestone(tagVersion, closedMilestones(version));
|
||||
Milestone.Smart foundMilestone = matchingMilestone(tagVersion, closedMilestones(version));
|
||||
String foundUrl = "";
|
||||
if (foundMilestone != null) {
|
||||
try {
|
||||
@@ -144,36 +149,51 @@ class GithubMilestones {
|
||||
return version.contains("RELEASE") ? version.substring(0, version.lastIndexOf(".")) : "";
|
||||
}
|
||||
|
||||
String milestoneTitle(GHMilestone milestone) throws IOException {
|
||||
return milestone.getTitle();
|
||||
String milestoneTitle(Milestone.Smart milestone) throws IOException {
|
||||
return milestone.title();
|
||||
}
|
||||
|
||||
URL foundMilestoneUrl(GHMilestone milestone) throws IOException {
|
||||
return milestone.getUrl();
|
||||
URL foundMilestoneUrl(Milestone.Smart milestone) throws IOException {
|
||||
return milestone.url();
|
||||
}
|
||||
|
||||
private Iterable<GHMilestone> openMilestones(ProjectVersion version) {
|
||||
private Iterable<Milestone> getMilestones(ProjectVersion version, Map<String, String> map) {
|
||||
try {
|
||||
return this.github.getRepository(org() + "/" + version.projectName).listMilestones(GHIssueState.OPEN)
|
||||
.toList();
|
||||
return this.github.repos().get(new Coordinates.Simple(org(), version.projectName)).milestones()
|
||||
.iterate(map);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
catch (AssertionError e) {
|
||||
log.error("Exception occurred while trying to fetch milestones", e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private Iterable<GHMilestone> closedMilestones(ProjectVersion version) {
|
||||
try {
|
||||
return this.github.getRepository(org() + "/" + version.projectName).listMilestones(GHIssueState.CLOSED)
|
||||
.toList();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
private Iterable<Milestone> openMilestones(ProjectVersion version) {
|
||||
return getMilestones(version, openMilestones());
|
||||
}
|
||||
|
||||
private Iterable<Milestone> closedMilestones(ProjectVersion version) {
|
||||
return getMilestones(version, closedMilestones());
|
||||
}
|
||||
|
||||
String org() {
|
||||
return this.properties.getGit().getOrgName();
|
||||
}
|
||||
|
||||
private Map<String, String> openMilestones() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("state", "open");
|
||||
params.put("sort", "due_on");
|
||||
params.put("direction", "desc");
|
||||
return params;
|
||||
}
|
||||
|
||||
private Map<String, String> closedMilestones() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("state", "closed");
|
||||
params.put("sort", "due_on");
|
||||
params.put("direction", "desc");
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,13 +52,13 @@ class RestTemplateSaganClient implements SaganClient {
|
||||
public Project getProject(String projectName) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.put("Accept", Collections.singletonList("application/hal+json"));
|
||||
Project project = this.restTemplate.exchange(this.baseUrl + "/projects/{projectName}", HttpMethod.GET,
|
||||
Project project = this.restTemplate.exchange(this.baseUrl + "/api/projects/{projectName}", HttpMethod.GET,
|
||||
new HttpEntity<>(headers), Project.class, projectName).getBody();
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
EmbeddedProjectReleases body = this.restTemplate.exchange(this.baseUrl + "/projects/{projectName}/releases",
|
||||
EmbeddedProjectReleases body = this.restTemplate.exchange(this.baseUrl + "/api/projects/{projectName}/releases",
|
||||
HttpMethod.GET, new HttpEntity<>(headers), EmbeddedProjectReleases.class, projectName).getBody();
|
||||
project.setReleases(body._embedded.releases);
|
||||
return project;
|
||||
@@ -68,14 +68,14 @@ class RestTemplateSaganClient implements SaganClient {
|
||||
public Release getRelease(String projectName, String releaseVersion) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.put("Accept", Collections.singletonList("application/hal+json"));
|
||||
return this.restTemplate.exchange(this.baseUrl + "/projects/{projectName}/releases/{releaseVersion}",
|
||||
return this.restTemplate.exchange(this.baseUrl + "/api/projects/{projectName}/releases/{releaseVersion}",
|
||||
HttpMethod.GET, new HttpEntity<>(headers), Release.class, projectName, releaseVersion).getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteRelease(String projectName, String releaseVersion) {
|
||||
ResponseEntity<Release> entity = this.restTemplate.exchange(
|
||||
this.baseUrl + "/projects/{projectName}/releases/{releaseVersion}", HttpMethod.DELETE,
|
||||
this.baseUrl + "/api/projects/{projectName}/releases/{releaseVersion}", HttpMethod.DELETE,
|
||||
new HttpEntity<>(""), Release.class, projectName, releaseVersion);
|
||||
boolean deleted = entity.getStatusCode().is2xxSuccessful();
|
||||
log.info("Response from Sagan\n\n[{}] \n with status [{}]", entity, entity.getStatusCode());
|
||||
@@ -85,7 +85,7 @@ class RestTemplateSaganClient implements SaganClient {
|
||||
@Override
|
||||
public boolean addRelease(String projectName, ReleaseInput releaseInput) {
|
||||
RequestEntity<ReleaseInput> request = RequestEntity
|
||||
.post(URI.create(this.baseUrl + "/projects/" + projectName + "/releases"))
|
||||
.post(URI.create(this.baseUrl + "/api/projects/" + projectName + "/releases"))
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE).body(releaseInput);
|
||||
ResponseEntity<Project> entity = this.restTemplate.exchange(request, Project.class);
|
||||
boolean added = entity.getStatusCode().is2xxSuccessful();
|
||||
@@ -94,12 +94,19 @@ class RestTemplateSaganClient implements SaganClient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void patchProjectDetails(String projectName, ProjectDetails details) {
|
||||
RequestEntity<ProjectDetails> request = RequestEntity
|
||||
.patch(URI.create(this.baseUrl + "/projects/" + projectName + "/details"))
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE).body(details);
|
||||
ResponseEntity<Object> entity = this.restTemplate.exchange(request, Object.class);
|
||||
log.info("Response from Sagan\n\n[{}]", entity);
|
||||
public Project patchProject(Project project) {
|
||||
// RequestEntity<Project> request = RequestEntity
|
||||
// .patch(URI.create(this.baseUrl + "/project_metadata/" + project.getSlug()))
|
||||
// .header(HttpHeaders.CONTENT_TYPE,
|
||||
// MediaType.APPLICATION_JSON_UTF8_VALUE).body(project);
|
||||
// ResponseEntity<Project> entity = this.restTemplate.exchange(request,
|
||||
// Project.class);
|
||||
// Project updatedProject = entity.getBody();
|
||||
// log.info("Response from Sagan\n\n[{}] \n with body [{}]", entity,
|
||||
// updatedProject);
|
||||
// return updatedProject;
|
||||
// FIXME: no api yet https://github.com/spring-io/sagan/issues/1052
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class EmbeddedProjectReleases {
|
||||
|
||||
@@ -29,6 +29,6 @@ public interface SaganClient {
|
||||
|
||||
boolean addRelease(String projectName, ReleaseInput releaseInput);
|
||||
|
||||
void patchProjectDetails(String projectName, ProjectDetails details);
|
||||
Project patchProject(Project project);
|
||||
|
||||
}
|
||||
|
||||
@@ -95,27 +95,30 @@ public class SaganUpdater {
|
||||
File docsModule = docsModule(projectFile);
|
||||
File indexDoc = new File(docsModule, this.releaserProperties.getSagan().getIndexSectionFileName());
|
||||
File bootDoc = new File(docsModule, this.releaserProperties.getSagan().getBootSectionFileName());
|
||||
ProjectDetails projectDetails = new ProjectDetails();
|
||||
if (indexDoc.exists()) {
|
||||
log.debug("Index adoc file exists");
|
||||
String fileText = fileToText(indexDoc);
|
||||
if (StringUtils.hasText(fileText)) {
|
||||
log.info("Index adoc content differs from the previously stored, will update it");
|
||||
projectDetails.setBody(fileText);
|
||||
shouldUpdate = true;
|
||||
}
|
||||
// if (StringUtils.hasText(fileText) && !fileText.equals(project.rawOverview))
|
||||
// {
|
||||
// log.info("Index adoc content differs from the previously stored, will
|
||||
// update it");
|
||||
// project.rawOverview = fileText;
|
||||
// shouldUpdate = true;
|
||||
// }
|
||||
}
|
||||
if (bootDoc.exists()) {
|
||||
log.debug("Boot adoc file exists");
|
||||
String fileText = fileToText(bootDoc);
|
||||
if (StringUtils.hasText(fileText)) {
|
||||
log.info("Boot adoc content differs from the previously stored, will update it");
|
||||
projectDetails.setBootConfig(fileText);
|
||||
shouldUpdate = true;
|
||||
}
|
||||
// if (StringUtils.hasText(fileText) &&
|
||||
// !fileText.equals(project.rawBootConfig)) {
|
||||
// log.info("Boot adoc content differs from the previously stored, will update
|
||||
// it");
|
||||
// project.rawBootConfig = fileText;
|
||||
// shouldUpdate = true;
|
||||
// }
|
||||
}
|
||||
if (shouldUpdate) {
|
||||
this.saganClient.patchProjectDetails(project.getName(), projectDetails);
|
||||
this.saganClient.patchProject(project);
|
||||
log.info("Updating Sagan project with adoc data.");
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.internal.github;
|
||||
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.Issues;
|
||||
import com.jcabi.github.Milestones;
|
||||
import com.jcabi.github.Releases;
|
||||
import com.jcabi.github.Repo;
|
||||
import com.jcabi.github.Repos;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class CachingGithubTests {
|
||||
|
||||
@Test
|
||||
void should_call_repos_only_once_for_same_github() {
|
||||
Github github = mock(Github.class);
|
||||
Repos repos = mock(Repos.class);
|
||||
given(github.repos()).willReturn(repos);
|
||||
CachingGithub cachingGithub = new CachingGithub(github);
|
||||
|
||||
cachingGithub.repos();
|
||||
cachingGithub.repos();
|
||||
cachingGithub.repos();
|
||||
|
||||
verify(github, only()).repos();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_repos_calls_for_different_githubs() {
|
||||
Github github1 = mock(Github.class);
|
||||
Github github2 = mock(Github.class);
|
||||
Github github3 = mock(Github.class);
|
||||
|
||||
new CachingGithub(github1).repos();
|
||||
new CachingGithub(github2).repos();
|
||||
new CachingGithub(github3).repos();
|
||||
|
||||
verify(github1).repos();
|
||||
verify(github2).repos();
|
||||
verify(github3).repos();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_get_repo_only_once_for_same_coordinates() {
|
||||
Repos repos = mock(Repos.class);
|
||||
CachingRepos cachingRepos = new CachingRepos(repos);
|
||||
Coordinates.Simple simple = new Coordinates.Simple("foo", "bar");
|
||||
|
||||
cachingRepos.get(simple);
|
||||
cachingRepos.get(simple);
|
||||
cachingRepos.get(simple);
|
||||
|
||||
verify(repos, only()).get(simple);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_calls_for_different_coordinates() {
|
||||
Repos repos = mock(Repos.class);
|
||||
CachingRepos cachingRepos = new CachingRepos(repos);
|
||||
Coordinates.Simple simple1 = new Coordinates.Simple("foo", "bar1");
|
||||
Coordinates.Simple simple2 = new Coordinates.Simple("foo", "bar2");
|
||||
Coordinates.Simple simple3 = new Coordinates.Simple("foo", "bar3");
|
||||
|
||||
cachingRepos.get(simple1);
|
||||
cachingRepos.get(simple2);
|
||||
cachingRepos.get(simple3);
|
||||
|
||||
verify(repos).get(simple1);
|
||||
verify(repos).get(simple2);
|
||||
verify(repos).get(simple3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_coordinates_only_once_for_same_repo() {
|
||||
Repo repo = mock(Repo.class);
|
||||
given(repo.coordinates()).willReturn(new Coordinates.Simple("foo", "bar"));
|
||||
CachingRepo cachingRepo = new CachingRepo(repo);
|
||||
|
||||
cachingRepo.coordinates();
|
||||
cachingRepo.coordinates();
|
||||
cachingRepo.coordinates();
|
||||
|
||||
verify(repo, only()).coordinates();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_calls_for_different_coordinates_for_repo() {
|
||||
Repo repo1 = mock(Repo.class);
|
||||
given(repo1.coordinates()).willReturn(new Coordinates.Simple("foo", "bar"));
|
||||
Repo repo2 = mock(Repo.class);
|
||||
given(repo2.coordinates()).willReturn(new Coordinates.Simple("foo", "bar"));
|
||||
Repo repo3 = mock(Repo.class);
|
||||
given(repo3.coordinates()).willReturn(new Coordinates.Simple("foo", "bar"));
|
||||
|
||||
new CachingRepo(repo1).coordinates();
|
||||
new CachingRepo(repo2).coordinates();
|
||||
new CachingRepo(repo3).coordinates();
|
||||
|
||||
verify(repo1).coordinates();
|
||||
verify(repo2).coordinates();
|
||||
verify(repo3).coordinates();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_issues_only_once_for_same_repo() {
|
||||
Repo repo = mock(Repo.class);
|
||||
given(repo.issues()).willReturn(BDDMockito.mock(Issues.class));
|
||||
CachingRepo cachingRepo = new CachingRepo(repo);
|
||||
|
||||
cachingRepo.issues();
|
||||
cachingRepo.issues();
|
||||
cachingRepo.issues();
|
||||
|
||||
verify(repo, only()).issues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_calls_for_different_issues_for_repo() {
|
||||
Repo repo1 = mock(Repo.class);
|
||||
given(repo1.issues()).willReturn(BDDMockito.mock(Issues.class));
|
||||
Repo repo2 = mock(Repo.class);
|
||||
given(repo2.issues()).willReturn(BDDMockito.mock(Issues.class));
|
||||
Repo repo3 = mock(Repo.class);
|
||||
given(repo3.issues()).willReturn(BDDMockito.mock(Issues.class));
|
||||
|
||||
new CachingRepo(repo1).issues();
|
||||
new CachingRepo(repo2).issues();
|
||||
new CachingRepo(repo3).issues();
|
||||
|
||||
verify(repo1).issues();
|
||||
verify(repo2).issues();
|
||||
verify(repo3).issues();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_call_milestones_only_once_for_same_repo() {
|
||||
Repo repo = mock(Repo.class);
|
||||
given(repo.milestones()).willReturn(BDDMockito.mock(Milestones.class));
|
||||
CachingRepo cachingRepo = new CachingRepo(repo);
|
||||
|
||||
cachingRepo.milestones();
|
||||
cachingRepo.milestones();
|
||||
cachingRepo.milestones();
|
||||
|
||||
verify(repo, only()).milestones();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_calls_for_different_milestones_for_repo() {
|
||||
Repo repo1 = mock(Repo.class);
|
||||
given(repo1.milestones()).willReturn(BDDMockito.mock(Milestones.class));
|
||||
Repo repo2 = mock(Repo.class);
|
||||
given(repo2.milestones()).willReturn(BDDMockito.mock(Milestones.class));
|
||||
Repo repo3 = mock(Repo.class);
|
||||
given(repo3.milestones()).willReturn(BDDMockito.mock(Milestones.class));
|
||||
|
||||
new CachingRepo(repo1).milestones();
|
||||
new CachingRepo(repo2).milestones();
|
||||
new CachingRepo(repo3).milestones();
|
||||
|
||||
verify(repo1).milestones();
|
||||
verify(repo2).milestones();
|
||||
verify(repo3).milestones();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_call_releases_only_once_for_same_repo() {
|
||||
Repo repo = mock(Repo.class);
|
||||
given(repo.releases()).willReturn(BDDMockito.mock(Releases.class));
|
||||
CachingRepo cachingRepo = new CachingRepo(repo);
|
||||
|
||||
cachingRepo.releases();
|
||||
cachingRepo.releases();
|
||||
cachingRepo.releases();
|
||||
|
||||
verify(repo, only()).releases();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_cache_calls_for_different_releases_for_repo() {
|
||||
Repo repo1 = mock(Repo.class);
|
||||
given(repo1.releases()).willReturn(BDDMockito.mock(Releases.class));
|
||||
Repo repo2 = mock(Repo.class);
|
||||
given(repo2.releases()).willReturn(BDDMockito.mock(Releases.class));
|
||||
Repo repo3 = mock(Repo.class);
|
||||
given(repo3.releases()).willReturn(BDDMockito.mock(Releases.class));
|
||||
|
||||
new CachingRepo(repo1).releases();
|
||||
new CachingRepo(repo2).releases();
|
||||
new CachingRepo(repo3).releases();
|
||||
|
||||
verify(repo1).releases();
|
||||
verify(repo2).releases();
|
||||
verify(repo3).releases();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.internal.github;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.mockito.BDDMockito;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.project.ProjectVersion;
|
||||
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
@WireMockTest(httpPort = 12346)
|
||||
class GithubIssueFillerTests {
|
||||
|
||||
private static final String ORG = "marcingrzejszczak";
|
||||
|
||||
private static final String REPO = "test-repo";
|
||||
|
||||
private static final String TOKEN = "FOO";
|
||||
|
||||
GitHub github;
|
||||
|
||||
ReleaserProperties properties = withToken();
|
||||
|
||||
GithubIssueFiler filer;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException {
|
||||
this.github = GitHub.connectToEnterprise("http://localhost:12346", TOKEN);
|
||||
this.filer = new GithubIssueFiler(github, properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_do_anything_for_non_release_train_version() {
|
||||
GitHub github = BDDMockito.mock(GitHub.class);
|
||||
|
||||
filer.fileAGitHubIssue(ORG, REPO, new ProjectVersion("foo", "1.0.0-SNAPSHOT"), "foo", "bar");
|
||||
|
||||
BDDMockito.then(github).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_file_an_issue_for_release_version(CapturedOutput capturedOutput) throws IOException {
|
||||
filer.fileAGitHubIssue(ORG, REPO, new ProjectVersion("foo", "1.0.0"), "foo", "bar");
|
||||
|
||||
BDDAssertions.then(capturedOutput.toString()).contains("Successfully created an issue");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_file_an_issue_for_release_version_if_one_is_already_created(CapturedOutput capturedOutput)
|
||||
throws IOException {
|
||||
new GithubIssueFiler(github, properties) {
|
||||
@Override
|
||||
boolean issueAlreadyFiled(GHRepository springGuides, String issueTitle) throws IOException {
|
||||
return true;
|
||||
}
|
||||
}.fileAGitHubIssue(ORG, REPO, new ProjectVersion("foo", "1.0.0"), "foo", "bar");
|
||||
|
||||
BDDAssertions.then(capturedOutput.toString()).doesNotContain("Successfully created an issue")
|
||||
.contains("Issue already filed, will not do that again");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_no_token_was_passed() {
|
||||
properties.getGit().setOauthToken("");
|
||||
|
||||
thenThrownBy(() -> filer.fileAGitHubIssue(ORG, REPO, new ProjectVersion("foo", "1.0.0"), "foo", "bar"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessageContaining("You have to pass Github OAuth token for milestone closing to be operational");
|
||||
}
|
||||
|
||||
ReleaserProperties withToken() {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setOrgName(ORG);
|
||||
properties.getGit().setOauthToken(TOKEN);
|
||||
properties.getPom().setBranch("vEdgware.RELEASE");
|
||||
properties.getGit().setUpdateSpringGuides(true);
|
||||
properties.getGit().setUpdateStartSpringIo(true);
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.internal.github;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
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.internal.project.ProjectVersion;
|
||||
import releaser.internal.project.Projects;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class GithubIssuesTests {
|
||||
|
||||
MkGithub github;
|
||||
|
||||
Repo repo;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws IOException {
|
||||
this.github = github("spring-guides");
|
||||
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);
|
||||
GithubIssues issues = new GithubIssues(Collections.emptyList());
|
||||
|
||||
issues.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_not_do_anything_if_not_applicable() {
|
||||
Github github = BDDMockito.mock(Github.class);
|
||||
GithubIssues issues = new GithubIssues(Collections.emptyList());
|
||||
|
||||
issues.fileIssueInSpringGuides(
|
||||
new Projects(new ProjectVersion("foo", "1.0.0.RELEASE"), new ProjectVersion("bar", "2.0.0.RELEASE"),
|
||||
new ProjectVersion("baz", "3.0.0.RELEASE")),
|
||||
new ProjectVersion("sc-release", "Edgware.RELEASE"));
|
||||
|
||||
BDDMockito.then(github).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_do_anything_for_non_release_train_version_when_updating_startspringio() throws IOException {
|
||||
setupStartSpringIo();
|
||||
Github github = BDDMockito.mock(Github.class);
|
||||
GithubIssues issues = new GithubIssues(Collections.emptyList());
|
||||
|
||||
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_not_do_anything_if_not_applicable_when_updating_startspringio() throws IOException {
|
||||
setupStartSpringIo();
|
||||
Github github = BDDMockito.mock(Github.class);
|
||||
GithubIssues issues = new GithubIssues(Collections.emptyList());
|
||||
|
||||
issues.fileIssueInStartSpringIo(
|
||||
new Projects(new ProjectVersion("foo", "1.0.0.RELEASE"), new ProjectVersion("bar", "2.0.0.RELEASE"),
|
||||
new ProjectVersion("baz", "3.0.0.RELEASE")),
|
||||
new ProjectVersion("sc-release", "Edgware.RELEASE"));
|
||||
|
||||
BDDMockito.then(github).shouldHaveNoInteractions();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,12 +19,13 @@ package releaser.internal.github;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
import com.jcabi.github.Milestone;
|
||||
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.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.kohsuke.github.GHMilestone;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.project.ProjectVersion;
|
||||
|
||||
@@ -38,23 +39,32 @@ import static org.assertj.core.api.BDDAssertions.thenThrownBy;
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
@WireMockTest(httpPort = 12345)
|
||||
class GithubMilestonesTests {
|
||||
public class GithubMilestonesTests {
|
||||
|
||||
private static final String ORG = "marcingrzejszczak";
|
||||
MkGithub github;
|
||||
|
||||
private static final String TOKEN = "foo";
|
||||
|
||||
GitHub github;
|
||||
Repo repo;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws IOException {
|
||||
this.github = GitHub.connectToEnterprise("http://localhost:12345", TOKEN);
|
||||
this.github = new MkGithub();
|
||||
this.repo = createSleuthRepo(this.github);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_close_milestone_if_there_is_one(CapturedOutput capturedOutput) throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
|
||||
public void should_close_milestone_if_there_is_one(CapturedOutput capturedOutput) throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.2.0.BUILD-SNAPSHOT";
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("0.2.0.BUILD-SNAPSHOT");
|
||||
|
||||
milestones.closeMilestone(nonGaSleuthProject());
|
||||
|
||||
@@ -62,126 +72,183 @@ class GithubMilestonesTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_close_milestone_when_the_milestone_contains_numeric_version_only_and_version_is_ga(
|
||||
public void should_close_milestone_when_the_milestone_contains_numeric_version_only_and_version_is_ga(
|
||||
CapturedOutput capturedOutput) throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
milestones.closeMilestone(gaProject());
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.2.0";
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("0.2.0");
|
||||
|
||||
milestones.closeMilestone(gaSleuthProject());
|
||||
|
||||
then(capturedOutput.toString()).doesNotContain("No matching milestone was found");
|
||||
}
|
||||
|
||||
private ProjectVersion closedProject() {
|
||||
return new ProjectVersion("test-repo", "0.1.0");
|
||||
}
|
||||
|
||||
private ProjectVersion gaProject() {
|
||||
return new ProjectVersion("test-repo", "0.0.1");
|
||||
private ProjectVersion gaSleuthProject() {
|
||||
return new ProjectVersion("spring-cloud-sleuth", "0.2.0.RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_close_milestone_when_the_milestone_contains_numeric_version_only(CapturedOutput capturedOutput)
|
||||
throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
|
||||
public void should_not_close_milestone_when_the_milestone_contains_numeric_version_only(
|
||||
CapturedOutput capturedOutput) throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
milestones.closeMilestone(notMatchingProjectVersion());
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.2.0";
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("0.2.0");
|
||||
|
||||
milestones.closeMilestone(nonGaSleuthProject());
|
||||
|
||||
then(capturedOutput.toString()).contains("No matching milestone was found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_fetch_url_of_a_closed_matching_milestone() throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
|
||||
|
||||
String url = milestones.milestoneUrl(closedProject());
|
||||
|
||||
then(url).isEqualTo("https://github.com/marcingrzejszczak/test-repo/milestone/3?closed=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_fetch_url_of_a_closed_matching_milestone_from_cache() {
|
||||
public void should_fetch_url_of_a_closed_matching_milestone() throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
GHMilestone matchingMilestone(String tagVersion, Iterable<GHMilestone> milestones) {
|
||||
throw new AssertionError("This should not be called");
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.2.0.RELEASE";
|
||||
}
|
||||
|
||||
@Override
|
||||
URL foundMilestoneUrl(Milestone.Smart milestone) throws IOException {
|
||||
return new URL("https://api.github.com/repos/spring-cloud/spring-cloud-sleuth/milestones/33");
|
||||
}
|
||||
};
|
||||
GithubMilestones.MILESTONE_URL_CACHE.put(gaProject(),
|
||||
"https://github.com/marcingrzejszczak/test-repo/milestone/3?closed=1");
|
||||
this.repo.milestones().create("0.2.0.RELEASE");
|
||||
|
||||
String url = milestones.milestoneUrl(gaProject());
|
||||
String url = milestones.milestoneUrl(gaSleuthProject());
|
||||
|
||||
then(url).isEqualTo("https://github.com/marcingrzejszczak/test-repo/milestone/3?closed=1");
|
||||
then(url).isEqualTo("https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_return_null_if_no_matching_milestone_was_found() {
|
||||
public void should_fetch_url_of_a_closed_matching_milestone_from_cache() {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken());
|
||||
GithubMilestones.MILESTONE_URL_CACHE.put(gaSleuthProject(),
|
||||
"https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
|
||||
|
||||
String url = milestones.milestoneUrl(gaSleuthProject());
|
||||
|
||||
then(url).isEqualTo("https://github.com/spring-cloud/spring-cloud-sleuth/milestone/33?closed=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_null_if_no_matching_milestone_was_found() {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String milestoneTitle(GHMilestone milestone) {
|
||||
return "0.9.0";
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
@Override
|
||||
URL foundMilestoneUrl(GHMilestone milestone) throws IOException {
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.9.0.RELEASE";
|
||||
}
|
||||
|
||||
@Override
|
||||
URL foundMilestoneUrl(Milestone.Smart milestone) throws IOException {
|
||||
return new URL("http://www.foo.com/bar");
|
||||
}
|
||||
};
|
||||
|
||||
String url = milestones.milestoneUrl(gaProject());
|
||||
String url = milestones.milestoneUrl(gaSleuthProject());
|
||||
|
||||
then(url).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_return_null_if_no_matching_milestone_was_found_within_threshold(CapturedOutput capturedOutput)
|
||||
public void should_return_null_if_no_matching_milestone_was_found_within_threshold(CapturedOutput capturedOutput)
|
||||
throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withThreshold());
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withThreshold()) {
|
||||
@Override
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
milestones.closeMilestone(gaProject());
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.2.0";
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("0.2.0");
|
||||
|
||||
milestones.closeMilestone(gaSleuthProject());
|
||||
|
||||
then(capturedOutput.toString()).contains("No matching milestones were found within the provided threshold [0]");
|
||||
}
|
||||
|
||||
private ProjectVersion nonGaSleuthProject() {
|
||||
return new ProjectVersion("test-repo", "0.0.1-SNAPSHOT");
|
||||
}
|
||||
|
||||
private ProjectVersion notMatchingProjectVersion() {
|
||||
return new ProjectVersion("test-repo", "0.0.100-SNAPSHOT");
|
||||
return new ProjectVersion("spring-cloud-sleuth", "0.2.0.BUILD-SNAPSHOT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_there_is_no_matching_milestone(CapturedOutput capturedOutput) throws IOException {
|
||||
public void should_throw_exception_when_there_is_no_matching_milestone(CapturedOutput capturedOutput)
|
||||
throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String org() {
|
||||
return ORG;
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
@Override
|
||||
String milestoneTitle(Milestone.Smart milestone) {
|
||||
return "0.1.0.BUILD-SNAPSHOT";
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("v0.2.0.BUILD-SNAPSHOT");
|
||||
|
||||
milestones.closeMilestone(notMatchingProjectVersion());
|
||||
milestones.closeMilestone(nonGaSleuthProject());
|
||||
then(capturedOutput.toString()).contains("No matching milestone was found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_print_that_no_milestones_were_found_when_io_problems_occurred(CapturedOutput capturedOutput)
|
||||
public void should_print_that_no_milestones_were_found_when_io_problems_occurred(CapturedOutput capturedOutput)
|
||||
throws IOException {
|
||||
GithubMilestones milestones = new GithubMilestones(this.github, withToken()) {
|
||||
@Override
|
||||
String org() {
|
||||
return GithubMilestonesTests.this.repo.coordinates().user();
|
||||
}
|
||||
|
||||
@Override
|
||||
GHMilestone matchingMilestone(String tagVersion, Iterable<GHMilestone> milestones) {
|
||||
return null;
|
||||
String milestoneTitle(Milestone.Smart milestone) throws IOException {
|
||||
throw new IOException("foo");
|
||||
}
|
||||
};
|
||||
this.repo.milestones().create("v0.2.0.BUILD-SNAPSHOT");
|
||||
|
||||
milestones.closeMilestone(nonGaSleuthProject());
|
||||
|
||||
then(capturedOutput.toString()).contains("No matching milestone was found");
|
||||
}
|
||||
|
||||
private Repo createSleuthRepo(MkGithub github) throws IOException {
|
||||
return github.repos().create(new Repos.RepoCreate("spring-cloud-sleuth", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_no_token_was_passed() {
|
||||
public void should_throw_exception_when_no_token_was_passed() {
|
||||
GithubMilestones milestones = new GithubMilestones(new ReleaserProperties());
|
||||
|
||||
thenThrownBy(() -> milestones.closeMilestone(nonGaSleuthProject())).isInstanceOf(IllegalArgumentException.class)
|
||||
@@ -190,16 +257,13 @@ class GithubMilestonesTests {
|
||||
|
||||
ReleaserProperties withToken() {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setOauthToken(TOKEN);
|
||||
properties.getGit().setOrgName(ORG);
|
||||
properties.getGit().setOauthToken("foo");
|
||||
return properties;
|
||||
}
|
||||
|
||||
ReleaserProperties withThreshold() {
|
||||
ReleaserProperties properties = withToken();
|
||||
properties.getGit().setNumberOfCheckedMilestones(0);
|
||||
properties.getGit().setOauthToken(TOKEN);
|
||||
properties.getGit().setOrgName(ORG);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,19 @@ package releaser.internal.sagan;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
@@ -32,21 +38,30 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@WireMockTest(httpPort = 23456)
|
||||
class RestTemplateSaganClientTests {
|
||||
@Disabled
|
||||
@SpringBootTest(classes = RestTemplateSaganClientTests.Config.class)
|
||||
@AutoConfigureStubRunner(ids = "io.spring.sagan:sagan-site")
|
||||
public class RestTemplateSaganClientTests {
|
||||
|
||||
RestTemplateSaganClient client;
|
||||
@Value("${stubrunner.runningstubs.sagan-site.port}")
|
||||
Integer saganPort;
|
||||
|
||||
@Autowired
|
||||
ObjectMapper objectMapper;
|
||||
|
||||
SaganClient client;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
public void setup() {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getGit().setOauthToken("foo");
|
||||
properties.getSagan().setBaseUrl("http://localhost:23456");
|
||||
properties.getSagan().setBaseUrl("http://localhost:" + this.saganPort);
|
||||
this.client = saganClient(properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_get_a_project() {
|
||||
@Disabled("TODO: The API has changed")
|
||||
public void should_get_a_project() {
|
||||
Project project = this.client.getProject("spring-boot");
|
||||
|
||||
then(project.getSlug()).isEqualTo("spring-boot");
|
||||
@@ -55,54 +70,82 @@ class RestTemplateSaganClientTests {
|
||||
then(project.getStatus()).isEqualTo("ACTIVE");
|
||||
then(project.getReleases()).isNotEmpty();
|
||||
Release release = project.getReleases().get(0);
|
||||
then(release.getStatus()).isEqualTo("GENERAL_AVAILABILITY");
|
||||
then(release.getStatus()).isEqualTo("PRERELEASE");
|
||||
then(release.getReferenceDocUrl())
|
||||
.isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/reference/html/");
|
||||
then(release.getApiDocUrl()).isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/api/");
|
||||
then(release.getVersion()).isEqualTo("2.5.14");
|
||||
.isEqualTo("https://docs.spring.io/spring-boot/docs/2.4.0-M1/reference/html/");
|
||||
then(release.getApiDocUrl()).isEqualTo("https://docs.spring.io/spring-boot/docs/2.4.0-M1/api/");
|
||||
then(release.getVersion()).isEqualTo("2.4.0-M1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_get_a_release() {
|
||||
Release release = this.client.getRelease("spring-boot", "2.5.14");
|
||||
@Disabled("TODO: The API has changed")
|
||||
public void should_get_a_release() {
|
||||
Release release = this.client.getRelease("spring-boot", "2.3.0.RELEASE");
|
||||
|
||||
then(release.getStatus()).isEqualTo("GENERAL_AVAILABILITY");
|
||||
then(release.getReferenceDocUrl())
|
||||
.isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/reference/html/");
|
||||
then(release.getApiDocUrl()).isEqualTo("https://docs.spring.io/spring-boot/docs/{version}/api/");
|
||||
then(release.getVersion()).isEqualTo("2.5.14");
|
||||
then(release.getReferenceDocUrl()).isEqualTo("https://docs.spring.io/spring-boot/docs/current/reference/html/");
|
||||
then(release.getApiDocUrl()).isEqualTo("https://docs.spring.io/spring-boot/docs/current/api/");
|
||||
then(release.getVersion()).isEqualTo("2.3.0.RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_delete_a_release() {
|
||||
boolean deleted = this.client.deleteRelease("spring-cloud-contract", "4.0.3-SNAPSHOT");
|
||||
@Disabled("TODO: The API has changed")
|
||||
public void should_delete_a_release() {
|
||||
boolean deleted = this.client.deleteRelease("spring-boot", "2.3.0.RELEASE");
|
||||
|
||||
then(deleted).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_a_release() {
|
||||
@Disabled("TODO: The API has changed")
|
||||
public void should_update_a_release() {
|
||||
|
||||
ReleaseInput releaseInput = new ReleaseInput();
|
||||
releaseInput.setVersion("4.0.3-SNAPSHOT");
|
||||
releaseInput.setReferenceDocUrl("https://docs.spring.io/spring-cloud-contract/docs/{version}/reference/html/");
|
||||
releaseInput.setApiDocUrl("https://docs.spring.io/spring-cloud-contract/docs/{version}/api/");
|
||||
|
||||
boolean added = this.client.addRelease("spring-cloud-contract", releaseInput);
|
||||
releaseInput.setVersion("2.2.0.RELEASE");
|
||||
releaseInput.setReferenceDocUrl("https://docs.spring.io/spring-boot/docs/{version}/reference/html/");
|
||||
releaseInput.setApiDocUrl("https://docs.spring.io/spring-boot/docs/{version}/api/");
|
||||
|
||||
boolean added = this.client.addRelease("spring-boot", releaseInput);
|
||||
then(added).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_patch_a_project() throws IOException {
|
||||
ProjectDetails projectDetails = new ProjectDetails();
|
||||
projectDetails.setBody("new body");
|
||||
projectDetails.setBootConfig("new sbc");
|
||||
@Disabled("no api yet https://github.com/spring-io/sagan/issues/1052")
|
||||
public void should_patch_a_project() throws IOException {
|
||||
String projectJson = "{\n \"id\" : \"spring-framework\",\n "
|
||||
+ "\"rawBootConfig\" : \"rawBootConfig\",\n \"rawOverview\" : \"rawOverview\",\n "
|
||||
+ "\"displayOrder\" : 2147483647,\n \"projectReleases\" : [ ],"
|
||||
+ "\n \"projectSamples\" : [ ],\n \"mostCurrentRelease\" : {\n \"present\" : false\n },"
|
||||
+ "\n \"nonMostCurrentReleases\" : [ ],\n \"stackOverflowTagList\" : [ ],\n \"topLevelProject\" : true\n}";
|
||||
Project project = this.objectMapper.readValue(projectJson, Project.class);
|
||||
|
||||
BDDAssertions.thenNoException()
|
||||
.isThrownBy(() -> this.client.patchProjectDetails("spring-cloud-contract", projectDetails));
|
||||
Project patchedProject = this.client.patchProject(project);
|
||||
|
||||
// then(patchedProject.id).isEqualTo("spring-framework");
|
||||
// then(patchedProject.name).isEqualTo("Spring Framework");
|
||||
// then(patchedProject.rawBootConfig).isEqualTo("rawBootConfig");
|
||||
// then(patchedProject.rawOverview).isEqualTo("rawOverview");
|
||||
}
|
||||
|
||||
private RestTemplateSaganClient saganClient(ReleaserProperties properties) {
|
||||
private Repository milestone() {
|
||||
Repository milestone = new Repository();
|
||||
milestone.id = "spring-milestones";
|
||||
milestone.name = "Spring Milestones";
|
||||
milestone.url = "https://repo.spring.io/libs-milestone";
|
||||
milestone.snapshotsEnabled = false;
|
||||
return milestone;
|
||||
}
|
||||
|
||||
private Repository snapshots() {
|
||||
Repository snapshots = new Repository();
|
||||
snapshots.id = "spring-snapshots";
|
||||
snapshots.name = "Spring Snapshots";
|
||||
snapshots.url = "https://repo.spring.io/libs-snapshot";
|
||||
snapshots.snapshotsEnabled = true;
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
private SaganClient saganClient(ReleaserProperties properties) {
|
||||
RestTemplate restTemplate = restTemplate(properties);
|
||||
return new RestTemplateSaganClient(restTemplate, properties);
|
||||
}
|
||||
@@ -111,4 +154,10 @@ class RestTemplateSaganClientTests {
|
||||
return new RestTemplateBuilder().basicAuthentication(properties.getGit().getOauthToken(), "").build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class SaganUpdaterTest {
|
||||
private Project project;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
public void setup() {
|
||||
project = new Project();
|
||||
project.setReleases(Arrays.asList(release("2.2.0-RC1"), release("2.3.0-SNAPSHOT"), release("2.2.0-M4")));
|
||||
this.properties.getSagan().setUpdateSagan(true);
|
||||
@@ -72,7 +72,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_sagan_when_switch_is_off() {
|
||||
public void should_not_update_sagan_when_switch_is_off() {
|
||||
this.properties.getSagan().setUpdateSagan(false);
|
||||
|
||||
ExecutionResult result = this.saganUpdater.updateSagan(new File("."), "main", version("2.2.0-M1"),
|
||||
@@ -83,7 +83,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_sagan_releases_for_milestone() {
|
||||
public void should_update_sagan_releases_for_milestone() {
|
||||
given(this.saganClient.addRelease(eq("foo"), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("2.2.0-M1"));
|
||||
|
||||
@@ -97,7 +97,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_sagan_releases_for_rc() {
|
||||
public void should_update_sagan_releases_for_rc() {
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("2.2.0-RC1"));
|
||||
|
||||
@@ -111,7 +111,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_docs_for_sagan_when_current_version_older() {
|
||||
public void should_not_update_docs_for_sagan_when_current_version_older() {
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("2.2.0-RC1"));
|
||||
|
||||
@@ -120,12 +120,12 @@ public class SaganUpdaterTest {
|
||||
assertThat(result.isSkipped()).isFalse();
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
|
||||
then(this.saganClient).should(never()).patchProjectDetails(anyString(), any(ProjectDetails.class));
|
||||
then(this.saganClient).should(never()).patchProject(any(Project.class));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_not_update_docs_for_sagan_when_files_exist_but_content_does_not_differ() throws IOException {
|
||||
public void should_not_update_docs_for_sagan_when_files_exist_but_content_does_not_differ() throws IOException {
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("3.0.0-RC1"));
|
||||
|
||||
@@ -144,11 +144,12 @@ public class SaganUpdaterTest {
|
||||
assertThat(result.isSkipped()).isFalse();
|
||||
assertThat(result.isSuccess()).isTrue();
|
||||
|
||||
then(this.saganClient).should(never()).patchProjectDetails(anyString(), any(ProjectDetails.class));
|
||||
then(this.saganClient).should(never()).patchProject(any(Project.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_docs_for_sagan_when_current_version_newer_and_only_overview_adoc_exists() throws IOException {
|
||||
public void should_update_docs_for_sagan_when_current_version_newer_and_only_overview_adoc_exists()
|
||||
throws IOException {
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("3.0.0-RC1"));
|
||||
|
||||
@@ -171,7 +172,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_docs_for_sagan_when_current_version_newer_and_only_boot_adoc_exists() throws IOException {
|
||||
public void should_update_docs_for_sagan_when_current_version_newer_and_only_boot_adoc_exists() throws IOException {
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("3.0.0-RC1"));
|
||||
|
||||
@@ -204,7 +205,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_sagan_from_main() {
|
||||
public void should_update_sagan_from_main() {
|
||||
ProjectVersion projectVersion = version("2.4.0-SNAPSHOT");
|
||||
given(this.saganClient.addRelease(anyString(), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("2.4.0-SNAPSHOT"));
|
||||
@@ -224,7 +225,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_sagan_from_release_version() {
|
||||
public void should_update_sagan_from_release_version() {
|
||||
ProjectVersion projectVersion = version("2.2.0");
|
||||
given(this.saganClient.addRelease(eq("foo"), any())).willReturn(true);
|
||||
given(this.saganClient.deleteRelease("foo", "2.2.0-RC1")).willReturn(true);
|
||||
@@ -247,7 +248,7 @@ public class SaganUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_update_sagan_from_non_main() {
|
||||
public void should_update_sagan_from_non_main() {
|
||||
ProjectVersion projectVersion = version("2.3.0-SNAPSHOT");
|
||||
given(this.saganClient.addRelease(eq("foo"), any())).willReturn(true);
|
||||
given(this.saganClient.getProject("foo")).willReturn(projectWithNewRelease("2.3.0-SNAPSHOT"));
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id" : "0b389dc6-1b73-487d-8122-41f1c974647f",
|
||||
"name" : "repos_marcingrzejszczak_test-repo_issues",
|
||||
"request" : {
|
||||
"url" : "/repos/marcingrzejszczak/test-repo/issues",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToJson" : "{\"assignees\":[],\"title\":\"foo\",\"body\":\"bar\",\"labels\":[]}",
|
||||
"ignoreArrayOrder" : true,
|
||||
"ignoreExtraElements" : true
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 201,
|
||||
"body" : "{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2\",\"repository_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2/labels{/name}\",\"comments_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2/comments\",\"events_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2/events\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/issues/2\",\"id\":1700204230,\"node_id\":\"I_kwDOJgNfic5lVw7G\",\"number\":2,\"title\":\"foo\",\"user\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"labels\":[],\"state\":\"open\",\"locked\":false,\"assignee\":null,\"assignees\":[],\"milestone\":null,\"comments\":0,\"created_at\":\"2023-05-08T12:57:04Z\",\"updated_at\":\"2023-05-08T12:57:04Z\",\"closed_at\":null,\"author_association\":\"OWNER\",\"active_lock_reason\":null,\"body\":\"bar\",\"closed_by\":null,\"reactions\":{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2/reactions\",\"total_count\":0,\"+1\":0,\"-1\":0,\"laugh\":0,\"hooray\":0,\"confused\":0,\"heart\":0,\"rocket\":0,\"eyes\":0},\"timeline_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/2/timeline\",\"performed_via_github_app\":null,\"state_reason\":null}"
|
||||
},
|
||||
"uuid" : "0b389dc6-1b73-487d-8122-41f1c974647f",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 63
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id" : "a494c3df-1e2a-45da-bd13-fec861e0bf80",
|
||||
"name" : "repos_marcingrzejszczak_test-repo_issues",
|
||||
"request" : {
|
||||
"url" : "/repos/marcingrzejszczak/test-repo/issues?state=open",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "[{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1\",\"repository_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1/labels{/name}\",\"comments_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1/comments\",\"events_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1/events\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/issues/1\",\"id\":1700018397,\"node_id\":\"I_kwDOJgNfic5lVDjd\",\"number\":1,\"title\":\"Issue\",\"user\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"labels\":[],\"state\":\"open\",\"locked\":false,\"assignee\":null,\"assignees\":[],\"milestone\":{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/milestone/1\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1/labels\",\"id\":9372216,\"node_id\":\"MI_kwDOJgNfic4AjwI4\",\"number\":1,\"title\":\"0.0.1\",\"description\":null,\"creator\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"open_issues\":1,\"closed_issues\":0,\"state\":\"open\",\"created_at\":\"2023-05-08T10:44:51Z\",\"updated_at\":\"2023-05-08T11:49:24Z\",\"due_on\":null,\"closed_at\":null},\"comments\":0,\"created_at\":\"2023-05-08T10:44:53Z\",\"updated_at\":\"2023-05-08T10:44:53Z\",\"closed_at\":null,\"author_association\":\"OWNER\",\"active_lock_reason\":null,\"body\":null,\"reactions\":{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1/reactions\",\"total_count\":0,\"+1\":0,\"-1\":0,\"laugh\":0,\"hooray\":0,\"confused\":0,\"heart\":0,\"rocket\":0,\"eyes\":0},\"timeline_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/issues/1/timeline\",\"performed_via_github_app\":null,\"state_reason\":null}]"
|
||||
},
|
||||
"uuid" : "a494c3df-1e2a-45da-bd13-fec861e0bf80",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 62
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id" : "384801a9-2b6a-4f8b-a259-fc554383b4b5",
|
||||
"name" : "repos_marcingrzejszczak_test-repo_milestones",
|
||||
"request" : {
|
||||
"url" : "/repos/marcingrzejszczak/test-repo/milestones?state=open",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "[{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/milestone/1\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1/labels\",\"id\":9372216,\"node_id\":\"MI_kwDOJgNfic4AjwI4\",\"number\":1,\"title\":\"0.0.1\",\"description\":null,\"creator\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"open_issues\":1,\"closed_issues\":0,\"state\":\"open\",\"created_at\":\"2023-05-08T10:44:51Z\",\"updated_at\":\"2023-05-08T11:49:24Z\",\"due_on\":null,\"closed_at\":null},{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/2\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/milestone/2\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/2/labels\",\"id\":9372221,\"node_id\":\"MI_kwDOJgNfic4AjwI9\",\"number\":2,\"title\":\"0.0.1-SNAPSHOT\",\"description\":\"\",\"creator\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"open_issues\":0,\"closed_issues\":0,\"state\":\"open\",\"created_at\":\"2023-05-08T10:47:21Z\",\"updated_at\":\"2023-05-08T11:50:14Z\",\"due_on\":null,\"closed_at\":null}]"
|
||||
},
|
||||
"uuid" : "384801a9-2b6a-4f8b-a259-fc554383b4b5",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 44
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"id" : "ec859ab2-e3fc-45ab-b913-3c6094548d0b",
|
||||
"name" : "repos_marcingrzejszczak_test-repo_milestones",
|
||||
"request" : {
|
||||
"url" : "/repos/marcingrzejszczak/test-repo/milestones?state=closed",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "[{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/3\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/milestone/3\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/3/labels\",\"id\":9372427,\"node_id\":\"MI_kwDOJgNfic4AjwML\",\"number\":3,\"title\":\"0.1.0\",\"description\":\"Closed\",\"creator\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"open_issues\":0,\"closed_issues\":0,\"state\":\"closed\",\"created_at\":\"2023-05-08T11:50:54Z\",\"updated_at\":\"2023-05-08T11:51:14Z\",\"due_on\":null,\"closed_at\":\"2023-05-08T11:51:14Z\"}]"
|
||||
},
|
||||
"uuid" : "ec859ab2-e3fc-45ab-b913-3c6094548d0b",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 40
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"id" : "1ffed929-9781-4f60-a942-c71a5bbb8550",
|
||||
"name" : "repos_marcingrzejszczak_test-repo_milestones_1",
|
||||
"request" : {
|
||||
"url" : "/repos/marcingrzejszczak/test-repo/milestones/1",
|
||||
"method" : "PATCH",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToJson" : "{\"state\":\"closed\"}",
|
||||
"ignoreArrayOrder" : true,
|
||||
"ignoreExtraElements" : true
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "{\"url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1\",\"html_url\":\"https://github.com/marcingrzejszczak/test-repo/milestone/1\",\"labels_url\":\"https://api.github.com/repos/marcingrzejszczak/test-repo/milestones/1/labels\",\"id\":9372216,\"node_id\":\"MI_kwDOJgNfic4AjwI4\",\"number\":1,\"title\":\"0.0.1\",\"description\":null,\"creator\":{\"login\":\"marcingrzejszczak\",\"id\":3297437,\"node_id\":\"MDQ6VXNlcjMyOTc0Mzc=\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/3297437?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/marcingrzejszczak\",\"html_url\":\"https://github.com/marcingrzejszczak\",\"followers_url\":\"https://api.github.com/users/marcingrzejszczak/followers\",\"following_url\":\"https://api.github.com/users/marcingrzejszczak/following{/other_user}\",\"gists_url\":\"https://api.github.com/users/marcingrzejszczak/gists{/gist_id}\",\"starred_url\":\"https://api.github.com/users/marcingrzejszczak/starred{/owner}{/repo}\",\"subscriptions_url\":\"https://api.github.com/users/marcingrzejszczak/subscriptions\",\"organizations_url\":\"https://api.github.com/users/marcingrzejszczak/orgs\",\"repos_url\":\"https://api.github.com/users/marcingrzejszczak/repos\",\"events_url\":\"https://api.github.com/users/marcingrzejszczak/events{/privacy}\",\"received_events_url\":\"https://api.github.com/users/marcingrzejszczak/received_events\",\"type\":\"User\",\"site_admin\":false},\"open_issues\":1,\"closed_issues\":0,\"state\":\"closed\",\"created_at\":\"2023-05-08T10:44:51Z\",\"updated_at\":\"2023-05-08T11:45:14Z\",\"due_on\":null,\"closed_at\":\"2023-05-08T11:45:14Z\"}"
|
||||
},
|
||||
"uuid" : "1ffed929-9781-4f60-a942-c71a5bbb8550",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 26
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"id" : "ab37a4f8-9c96-46d0-8aab-07a352e8ea3f",
|
||||
"name" : "user",
|
||||
"request" : {
|
||||
"url" : "/user",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200
|
||||
},
|
||||
"uuid" : "ab37a4f8-9c96-46d0-8aab-07a352e8ea3f",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 23
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id" : "a4099665-5a81-4767-80f0-ed9b5a60121e",
|
||||
"name" : "projects_spring-boot",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-boot",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "{\"name\":\"Spring Boot\",\"slug\":\"spring-boot\",\"repositoryUrl\":\"https://github.com/spring-projects/spring-boot\",\"status\":\"ACTIVE\",\"_links\":{\"releases\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases\"},\"generations\":{\"href\":\"https://api.spring.io/projects/spring-boot/generations\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot\"}}}",
|
||||
"headers" : {
|
||||
"Content-Type" : "application/hal+json"
|
||||
}
|
||||
},
|
||||
"uuid" : "a4099665-5a81-4767-80f0-ed9b5a60121e",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 1
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id" : "740428fb-e86b-46fb-9544-68cfbc7f335e",
|
||||
"name" : "projects_spring-boot_releases",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-boot/releases",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "{\"_embedded\":{\"releases\":[{\"version\":\"2.5.14\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"GENERAL_AVAILABILITY\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-releases\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/2.5.14\"}}},{\"version\":\"2.6.14\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"GENERAL_AVAILABILITY\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-releases\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/2.6.14\"}}},{\"version\":\"2.7.12-SNAPSHOT\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"SNAPSHOT\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-snapshots\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/2.7.12-SNAPSHOT\"}}},{\"version\":\"3.1.0-SNAPSHOT\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"SNAPSHOT\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-snapshots\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/3.1.0-SNAPSHOT\"}}},{\"version\":\"3.1.0-M2\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"PRERELEASE\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-milestones\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/3.1.0-M2\"}}},{\"version\":\"3.0.6\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"GENERAL_AVAILABILITY\",\"current\":true,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-releases\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/3.0.6\"}}},{\"version\":\"3.0.7-SNAPSHOT\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"SNAPSHOT\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-snapshots\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/3.0.7-SNAPSHOT\"}}},{\"version\":\"2.7.11\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"GENERAL_AVAILABILITY\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-releases\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/2.7.11\"}}},{\"version\":\"3.1.0-RC2\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"PRERELEASE\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-milestones\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/3.1.0-RC2\"}}}]},\"_links\":{\"project\":{\"href\":\"https://api.spring.io/projects/spring-boot\"},\"current\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/current\"}}}",
|
||||
"headers" : {
|
||||
"Content-Type" : "application/hal+json"
|
||||
}
|
||||
},
|
||||
"uuid" : "740428fb-e86b-46fb-9544-68cfbc7f335e",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 2
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"id" : "5e584f53-bb48-4c2f-ad6d-c56c5b1fb0c5",
|
||||
"name" : "projects_spring-boot_releases_2514",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-boot/releases/2.5.14",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "{\"version\":\"2.5.14\",\"apiDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/api/\",\"referenceDocUrl\":\"https://docs.spring.io/spring-boot/docs/{version}/reference/html/\",\"status\":\"GENERAL_AVAILABILITY\",\"current\":false,\"_links\":{\"repository\":{\"href\":\"https://api.spring.io/repositories/spring-releases\"},\"self\":{\"href\":\"https://api.spring.io/projects/spring-boot/releases/2.5.14\"}}}",
|
||||
"headers" : {
|
||||
"Content-Type" : "application/hal+json"
|
||||
}
|
||||
},
|
||||
"uuid" : "5e584f53-bb48-4c2f-ad6d-c56c5b1fb0c5",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 4
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id" : "048e10c5-8a54-4e77-8ee4-0c1cc14b1eff",
|
||||
"name" : "projects_spring-cloud-contract_details",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-cloud-contract/details",
|
||||
"method" : "PATCH",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToJson" : "{\"bootConfig\":\"new sbc\",\"body\":\"new body\"}",
|
||||
"ignoreArrayOrder" : true,
|
||||
"ignoreExtraElements" : true
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 204
|
||||
},
|
||||
"uuid" : "048e10c5-8a54-4e77-8ee4-0c1cc14b1eff",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 16
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"id" : "d26df67c-4167-4e85-bab9-dfa95dabe806",
|
||||
"name" : "projects_spring-cloud-contract_releases",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-cloud-contract/releases",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToJson" : "{\"version\":\"4.0.3-SNAPSHOT\",\"referenceDocUrl\":\"https://docs.spring.io/spring-cloud-contract/docs/{version}/reference/html/\",\"apiDocUrl\":\"https://docs.spring.io/spring-cloud-contract/docs/{version}/api/\"}",
|
||||
"ignoreArrayOrder" : true,
|
||||
"ignoreExtraElements" : true
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 201
|
||||
},
|
||||
"uuid" : "d26df67c-4167-4e85-bab9-dfa95dabe806",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 15
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"id" : "d6fa2037-b021-44ab-9019-ec70c702f623",
|
||||
"name" : "projects_spring-cloud-contract_releases_403-snapshot",
|
||||
"request" : {
|
||||
"url" : "/projects/spring-cloud-contract/releases/4.0.3-SNAPSHOT",
|
||||
"method" : "DELETE"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 204
|
||||
},
|
||||
"uuid" : "d6fa2037-b021-44ab-9019-ec70c702f623",
|
||||
"persistent" : true,
|
||||
"insertionIndex" : 14
|
||||
}
|
||||
@@ -10,13 +10,13 @@
|
||||
<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>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.internal.github;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.RtGithub;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
class GithubConfiguration {
|
||||
|
||||
@Bean
|
||||
BeanPostProcessor cachingGithubBeanPostProcessor() {
|
||||
return new BeanPostProcessor() {
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof RtGithub) {
|
||||
return new CachingGithub((Github) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,8 +66,8 @@ class SaganConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void patchProjectDetails(String projectName, ProjectDetails details) {
|
||||
|
||||
public Project patchProject(Project project) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,15 +16,24 @@
|
||||
|
||||
package releaser.internal.spring;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.MapperFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
|
||||
import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
|
||||
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -34,6 +43,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
class BatchConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -66,11 +76,11 @@ class BatchConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
FlowRunner flowRunner(JobRepository jobRepository, PlatformTransactionManager manager,
|
||||
FlowRunner flowRunner(StepBuilderFactory stepBuilderFactory, JobBuilderFactory jobBuilderFactory,
|
||||
ProjectsToRunFactory projectsToRunFactory, JobLauncher jobLauncher,
|
||||
FlowRunnerTaskExecutorSupplier flowRunnerTaskExecutorSupplier, ConfigurableApplicationContext context,
|
||||
ReleaserProperties releaserProperties, BuildReportHandler reportHandler) {
|
||||
return new SpringBatchFlowRunner(jobRepository, manager, projectsToRunFactory, jobLauncher,
|
||||
return new SpringBatchFlowRunner(stepBuilderFactory, jobBuilderFactory, projectsToRunFactory, jobLauncher,
|
||||
flowRunnerTaskExecutorSupplier, context, releaserProperties, reportHandler);
|
||||
}
|
||||
|
||||
@@ -85,28 +95,32 @@ class BatchConfiguration {
|
||||
serializer.setObjectMapper(objectMapper);
|
||||
return serializer;
|
||||
}
|
||||
/*
|
||||
* // Needed to add this to serialize the exceptions
|
||||
*
|
||||
* @Bean BatchConfigurer myBatchConfigurer(DataSource dataSource,
|
||||
* Jackson2ExecutionContextStringSerializer
|
||||
* myJackson2ExecutionContextStringSerializer, PlatformTransactionManager
|
||||
* transactionManager) { return new DefaultBatchConfigurer(dataSource) {
|
||||
*
|
||||
* @Override protected JobExplorer createJobExplorer() throws Exception {
|
||||
* JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
|
||||
* jobExplorerFactoryBean.setDataSource(dataSource);
|
||||
* jobExplorerFactoryBean.setSerializer(myJackson2ExecutionContextStringSerializer);
|
||||
* jobExplorerFactoryBean.afterPropertiesSet(); return
|
||||
* jobExplorerFactoryBean.getObject(); }
|
||||
*
|
||||
* @Override protected JobRepository createJobRepository() throws Exception {
|
||||
* JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
|
||||
* jobRepositoryFactoryBean.setDataSource(dataSource);
|
||||
* jobRepositoryFactoryBean.setSerializer(myJackson2ExecutionContextStringSerializer);
|
||||
* jobRepositoryFactoryBean.setTransactionManager(transactionManager);
|
||||
* jobRepositoryFactoryBean.afterPropertiesSet(); return
|
||||
* jobRepositoryFactoryBean.getObject(); } }; }
|
||||
*/
|
||||
|
||||
// Needed to add this to serialize the exceptions
|
||||
@Bean
|
||||
BatchConfigurer myBatchConfigurer(DataSource dataSource,
|
||||
Jackson2ExecutionContextStringSerializer myJackson2ExecutionContextStringSerializer,
|
||||
PlatformTransactionManager transactionManager) {
|
||||
return new DefaultBatchConfigurer(dataSource) {
|
||||
@Override
|
||||
protected JobExplorer createJobExplorer() throws Exception {
|
||||
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
|
||||
jobExplorerFactoryBean.setDataSource(dataSource);
|
||||
jobExplorerFactoryBean.setSerializer(myJackson2ExecutionContextStringSerializer);
|
||||
jobExplorerFactoryBean.afterPropertiesSet();
|
||||
return jobExplorerFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobRepository createJobRepository() throws Exception {
|
||||
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
|
||||
jobRepositoryFactoryBean.setDataSource(dataSource);
|
||||
jobRepositoryFactoryBean.setSerializer(myJackson2ExecutionContextStringSerializer);
|
||||
jobRepositoryFactoryBean.setTransactionManager(transactionManager);
|
||||
jobRepositoryFactoryBean.afterPropertiesSet();
|
||||
return jobRepositoryFactoryBean.getObject();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package releaser.internal.spring;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import releaser.internal.tasks.ReleaserTask;
|
||||
@@ -25,7 +24,7 @@ import releaser.internal.tasks.ReleaserTask;
|
||||
* A report from running a task. Can be mapped to a row in a table for a single execution
|
||||
* of a release task.
|
||||
*/
|
||||
public class ExecutionResultReport implements Serializable {
|
||||
public class ExecutionResultReport {
|
||||
|
||||
private String projectName;
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package releaser.internal.spring;
|
||||
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
@@ -62,10 +61,8 @@ class SpringBatchBuildReportHandler implements BuildReportHandler {
|
||||
|
||||
private List<Table> buildTable(List<StepExecution> stepContexts) {
|
||||
return stepContexts.stream().map(step -> {
|
||||
String date = step.getStartTime()
|
||||
.format(new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss.SSS").toFormatter());
|
||||
long millis = ChronoUnit.MILLIS.between(step.getStartTime().toInstant(ZoneOffset.UTC),
|
||||
step.getEndTime().toInstant(ZoneOffset.UTC));
|
||||
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(step.getStartTime());
|
||||
long millis = ChronoUnit.MILLIS.between(step.getStartTime().toInstant(), step.getEndTime().toInstant());
|
||||
ExecutionContext context = step.getExecutionContext();
|
||||
ExecutionResultReport entity = (ExecutionResultReport) context.get("entity");
|
||||
if (entity == null) {
|
||||
|
||||
@@ -50,6 +50,8 @@ import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.StepExecutionListener;
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.job.builder.FlowBuilder;
|
||||
import org.springframework.batch.core.job.builder.FlowJobBuilder;
|
||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||
@@ -57,14 +59,11 @@ import org.springframework.batch.core.job.builder.JobFlowBuilder;
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
|
||||
@@ -74,9 +73,9 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
|
||||
private final ConsoleInputStepSkipper stepSkipper;
|
||||
|
||||
private final JobRepository jobRepository;
|
||||
private final StepBuilderFactory stepBuilderFactory;
|
||||
|
||||
private final PlatformTransactionManager manager;
|
||||
private final JobBuilderFactory jobBuilderFactory;
|
||||
|
||||
private final ProjectsToRunFactory projectsToRunFactory;
|
||||
|
||||
@@ -90,12 +89,12 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
|
||||
private final ReleaserProperties releaserProperties;
|
||||
|
||||
SpringBatchFlowRunner(JobRepository jobRepository, PlatformTransactionManager manager,
|
||||
SpringBatchFlowRunner(StepBuilderFactory stepBuilderFactory, JobBuilderFactory jobBuilderFactory,
|
||||
ProjectsToRunFactory projectsToRunFactory, JobLauncher jobLauncher,
|
||||
FlowRunnerTaskExecutorSupplier flowRunnerTaskExecutorSupplier, ConfigurableApplicationContext context,
|
||||
ReleaserProperties releaserProperties, BuildReportHandler reportHandler) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.manager = manager;
|
||||
this.stepBuilderFactory = stepBuilderFactory;
|
||||
this.jobBuilderFactory = jobBuilderFactory;
|
||||
this.projectsToRunFactory = projectsToRunFactory;
|
||||
this.jobLauncher = jobLauncher;
|
||||
this.flowRunnerTaskExecutorSupplier = flowRunnerTaskExecutorSupplier;
|
||||
@@ -111,7 +110,7 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
}
|
||||
|
||||
private Step createStep(ReleaserTask releaserTask, NamedArgumentsSupplier argsSupplier) {
|
||||
return new StepBuilder(argsSupplier.projectName + "_" + releaserTask.name(), jobRepository)
|
||||
return this.stepBuilderFactory.get(argsSupplier.projectName + "_" + releaserTask.name())
|
||||
.tasklet((contribution, chunkContext) -> {
|
||||
Arguments args = argsSupplier.get();
|
||||
FlowRunner.Decision decision = beforeTask(args.options, args.properties, releaserTask);
|
||||
@@ -139,7 +138,7 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
log.info("Skipping step [{}]", releaserTask.name());
|
||||
}
|
||||
return RepeatStatus.FINISHED;
|
||||
}, this.manager).listener(releaserListener(argsSupplier, releaserTask)).build();
|
||||
}).listener(releaserListener(argsSupplier, releaserTask)).build();
|
||||
}
|
||||
|
||||
private List<Throwable> addExceptionToErrors(List<Throwable> errors, RuntimeException exception) {
|
||||
@@ -305,7 +304,7 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
}
|
||||
|
||||
private Job buildJobForFlows(Iterator<StuffToRun> flowsIterator) {
|
||||
JobBuilder release = new JobBuilder("release_" + System.currentTimeMillis(), this.jobRepository);
|
||||
JobBuilder release = this.jobBuilderFactory.get("release_" + System.currentTimeMillis());
|
||||
StuffToRun stuffToRun = flowsIterator.next();
|
||||
Flow first = stuffToRun.flow;
|
||||
JobFlowBuilder start = release.start(first);
|
||||
@@ -363,7 +362,7 @@ class SpringBatchFlowRunner implements FlowRunner, Closeable {
|
||||
log.info("No release train post release tasks to run, will do nothing");
|
||||
return ExecutionResult.success();
|
||||
}
|
||||
Job job = new JobBuilder(name, this.jobRepository).start(flow).build().build();
|
||||
Job job = this.jobBuilderFactory.get(name).start(flow).build().build();
|
||||
return runJob(job);
|
||||
}
|
||||
|
||||
@@ -512,14 +511,14 @@ class ConsoleInputStepSkipper {
|
||||
public boolean skipStep() {
|
||||
String input = chosenOption();
|
||||
switch (input.toLowerCase()) {
|
||||
case "s":
|
||||
return true;
|
||||
case "q":
|
||||
reportHandler.reportBuildSummary();
|
||||
System.exit(SpringApplication.exit(this.context, () -> 0));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
case "s":
|
||||
return true;
|
||||
case "q":
|
||||
reportHandler.reportBuildSummary();
|
||||
System.exit(SpringApplication.exit(this.context, () -> 0));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
|
||||
@SpringBootTest(properties = "spring.batch.jdbc.initialize-schema=always")
|
||||
@SpringBootTest
|
||||
@ActiveProfiles("batch")
|
||||
class SpringBatchFlowRunnerTests {
|
||||
|
||||
|
||||
@@ -4,5 +4,4 @@ releaser:
|
||||
|
||||
spring:
|
||||
batch:
|
||||
jdbc:
|
||||
initialize-schema: always
|
||||
initialize-schema: always
|
||||
@@ -10,13 +10,13 @@
|
||||
<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>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -50,14 +50,14 @@ import releaser.internal.sagan.Project;
|
||||
import releaser.internal.sagan.Release;
|
||||
import releaser.internal.tasks.ReleaserTask;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
@@ -320,11 +320,11 @@ public abstract class AbstractSpringAcceptanceTests {
|
||||
public static class DefaultTestConfiguration {
|
||||
|
||||
@Bean
|
||||
SpringBatchFlowRunner mySpringBatchFlowRunner(JobRepository jobRepository, PlatformTransactionManager manager,
|
||||
ProjectsToRunFactory projectsToRunFactory, JobLauncher jobLauncher,
|
||||
SpringBatchFlowRunner mySpringBatchFlowRunner(StepBuilderFactory stepBuilderFactory,
|
||||
JobBuilderFactory jobBuilderFactory, ProjectsToRunFactory projectsToRunFactory, JobLauncher jobLauncher,
|
||||
FlowRunnerTaskExecutorSupplier flowRunnerTaskExecutorSupplier, ConfigurableApplicationContext context,
|
||||
ReleaserProperties releaserProperties, BuildReportHandler reportHandler) {
|
||||
return new SpringBatchFlowRunner(jobRepository, manager, projectsToRunFactory, jobLauncher,
|
||||
return new SpringBatchFlowRunner(stepBuilderFactory, jobBuilderFactory, projectsToRunFactory, jobLauncher,
|
||||
flowRunnerTaskExecutorSupplier, context, releaserProperties, reportHandler) {
|
||||
@Override
|
||||
Decision decide(Options options, ReleaserTask task) {
|
||||
|
||||
31
spring-cloud-info/README.md
Normal file
31
spring-cloud-info/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Spring Cloud Info
|
||||
|
||||
## Why?
|
||||
|
||||
The Spring Cloud Team gets a lot of inquiries from users like the following:
|
||||
|
||||
* What version of Spring Cloud should I use if I am using Spring Boot `x.x.x`?
|
||||
* When will Spring Cloud `[RELEASE]` be released?
|
||||
* What version of `spring-cloud-*` is in Spring Cloud `[RELEASE]`?
|
||||
|
||||
The answers to these questions can easily be found if you know where to look. The purpose of
|
||||
Spring Cloud Info is to make these answers easily accessible via a REST API that we can then use
|
||||
to expose this information in a user friendly way.
|
||||
|
||||
## The Nitty Gritty Details
|
||||
|
||||
Spring Cloud Info is deployed on Pivotal Cloud Foundry in the org `spring.io` and space `
|
||||
spring-cloud-issuebot-production`.
|
||||
|
||||
There is a `manifest.yml` file in the root of the project you can use to deploy the app
|
||||
simply by running `cf push`. It requires a Spring Cloud Services Config Server service
|
||||
named `config-server`. The config server should point to the configuration in the branch
|
||||
`spring-cloud-info-config` of the repo https://github.com/spring-cloud/spring-cloud-release-tools.
|
||||
Within the `application.yml` in that branch there needs to be one an encrypted oauth token for GitHub
|
||||
that is set via `spring.cloud.info.git.oauthtoken`. This token is used by Spring Cloud Info to fetch
|
||||
data from GitHub.
|
||||
|
||||
## REST API
|
||||
The rest API is documented via Spring Rest Docs and is published along with the app to Pivotal
|
||||
Cloud Foundry. You can find the REST API documentation at
|
||||
https://spring-cloud-info.apps.pcfone.io/docs/spring-cloud-info.html.
|
||||
7
spring-cloud-info/manifest.yml
Normal file
7
spring-cloud-info/manifest.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
applications:
|
||||
- name: spring-cloud-info
|
||||
memory: 2048M
|
||||
path: ./target/spring-cloud-info-2.0.0-SNAPSHOT.jar
|
||||
services:
|
||||
- config-server
|
||||
157
spring-cloud-info/pom.xml
Normal file
157
spring-cloud-info/pom.xml
Normal file
@@ -0,0 +1,157 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>releaser-parent</artifactId>
|
||||
<groupId>org.springframework.cloud.internal</groupId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-info</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven-core.version>3.8.5</maven-core.version>
|
||||
<javax.json-api.version>1.1.4</javax.json-api.version>
|
||||
<maven-model.version>3.8.5</maven-model.version>
|
||||
<scs.version>3.1.6.RELEASE</scs.version>
|
||||
<spring-asciidoctor-extensions.version>0.2.0-RELEASE</spring-asciidoctor-extensions.version>
|
||||
<spring-cloud-bom.version>2021.0.1</spring-cloud-bom.version>
|
||||
<spring-rest-docs.version>2.0.6.RELEASE</spring-rest-docs.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-core</artifactId>
|
||||
<version>${maven-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcabi</groupId>
|
||||
<artifactId>jcabi-github</artifactId>
|
||||
<version>${jcabi-github.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.json</groupId>
|
||||
<artifactId>javax.json-api</artifactId>
|
||||
<version>${javax.json-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model</artifactId>
|
||||
<version>${maven-model.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.pivotal.spring.cloud</groupId>
|
||||
<artifactId>spring-cloud-services-starter-config-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.pivotal.spring.cloud</groupId>
|
||||
<artifactId>spring-cloud-services-dependencies</artifactId>
|
||||
<version>${scs.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud-bom.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.spring.asciidoctor</groupId>
|
||||
<artifactId>spring-asciidoctor-extensions</artifactId>
|
||||
<version>0.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-asciidoctor</artifactId>
|
||||
<version>2.0.6.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>${maven-resources-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources-for-fatjar</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>
|
||||
${project.basedir}/src/main/resources/static/docs
|
||||
</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>
|
||||
${project.build.directory}/generated-docs
|
||||
</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
121
spring-cloud-info/src/main/asciidoc/spring-cloud-info.adoc
Normal file
121
spring-cloud-info/src/main/asciidoc/spring-cloud-info.adoc
Normal file
@@ -0,0 +1,121 @@
|
||||
# Rest API
|
||||
|
||||
## Spring Cloud Versions
|
||||
|
||||
Gets all the available Spring Cloud release trains.
|
||||
|
||||
### cURL Request
|
||||
|
||||
include::{snippets}/springcloudversions/curl-request.adoc[]
|
||||
|
||||
### HTTPie Request
|
||||
|
||||
include::{snippets}/springcloudversions/httpie-request.adoc[]
|
||||
|
||||
### HTTP Request
|
||||
|
||||
include::{snippets}/springcloudversions/http-request.adoc[]
|
||||
|
||||
### Response
|
||||
|
||||
include::{snippets}/springcloudversions/http-response.adoc[]
|
||||
|
||||
### Response Fields
|
||||
|
||||
include::{snippets}/springcloudversions/response-fields.adoc[]
|
||||
|
||||
## Spring Cloud Version Given Spring Boot Version
|
||||
|
||||
Gets the Spring Cloud release train version given a Spring Boot version.
|
||||
|
||||
### Path Parameters
|
||||
|
||||
include::{snippets}/springcloudversion/path-parameters.adoc[]
|
||||
|
||||
### cURL Request
|
||||
|
||||
include::{snippets}/springcloudversion/curl-request.adoc[]
|
||||
|
||||
### HTTPie Request
|
||||
|
||||
include::{snippets}/springcloudversion/httpie-request.adoc[]
|
||||
|
||||
### HTTP Request
|
||||
|
||||
include::{snippets}/springcloudversion/http-request.adoc[]
|
||||
|
||||
### HTTP Response
|
||||
|
||||
include::{snippets}/springcloudversion/http-response.adoc[]
|
||||
|
||||
### Response Fields
|
||||
|
||||
include::{snippets}/springcloudversion/response-fields.adoc[]
|
||||
|
||||
## Spring Cloud Project Versions
|
||||
|
||||
Get the Spring Cloud project versions for a given Spring Cloud release train.
|
||||
|
||||
### cURL Request
|
||||
|
||||
include::{snippets}/bomversions/curl-request.adoc[]
|
||||
|
||||
### HTTPie Request
|
||||
|
||||
include::{snippets}/bomversions/httpie-request.adoc[]
|
||||
|
||||
### HTTP Request
|
||||
|
||||
include::{snippets}/bomversions/http-request.adoc[]
|
||||
|
||||
### HTTP Response
|
||||
|
||||
include::{snippets}/bomversions/http-response.adoc[]
|
||||
|
||||
## Upcoming Spring Cloud Releases
|
||||
|
||||
Gets all the upcoming Spring Cloud releases.
|
||||
|
||||
### cURL Request
|
||||
|
||||
include::{snippets}/milestones/curl-request.adoc[]
|
||||
|
||||
### HTTPie Request
|
||||
|
||||
include::{snippets}/milestones/httpie-request.adoc[]
|
||||
|
||||
### HTTP Request
|
||||
|
||||
include::{snippets}/milestones/http-request.adoc[]
|
||||
|
||||
### HTTP Response
|
||||
|
||||
include::{snippets}/milestones/http-response.adoc[]
|
||||
|
||||
## Get Spring Cloud Release Date
|
||||
|
||||
Gets the tentative date given an upcoming Spring Cloud release train name.
|
||||
|
||||
### Path Parameters
|
||||
|
||||
include::{snippets}/milestoneduedate/path-parameters.adoc[]
|
||||
|
||||
### cURL Request
|
||||
|
||||
include::{snippets}/milestoneduedate/curl-request.adoc[]
|
||||
|
||||
### HTTPie Request
|
||||
|
||||
include::{snippets}/milestoneduedate/httpie-request.adoc[]
|
||||
|
||||
### HTTP Request
|
||||
|
||||
include::{snippets}/milestoneduedate/http-request.adoc[]
|
||||
|
||||
### HTTP Response
|
||||
|
||||
include::{snippets}/milestoneduedate/http-response.adoc[]
|
||||
|
||||
### Response Fields
|
||||
|
||||
include::{snippets}/milestoneduedate/response-fields.adoc[]
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class GithubPomReader {
|
||||
|
||||
private MavenXpp3Reader reader;
|
||||
|
||||
private RestTemplate rest;
|
||||
|
||||
public GithubPomReader(MavenXpp3Reader reader, RestTemplate rest) {
|
||||
this.reader = reader;
|
||||
this.rest = rest;
|
||||
}
|
||||
|
||||
public Model readPomFromUrl(String url) throws IOException, XmlPullParserException {
|
||||
StringReader pomString = new StringReader(rest.getForObject(url, String.class));
|
||||
try {
|
||||
return reader.read(pomString);
|
||||
}
|
||||
finally {
|
||||
pomString.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cloud.info.exceptions.InitializrParseException;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudVersionNotFoundException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class InitializrSpringCloudInfoService extends SpringCloudRelease {
|
||||
|
||||
private static final String INITIALIZR_URL = "https://start.spring.io/actuator/info";
|
||||
|
||||
private RestTemplate rest;
|
||||
|
||||
public InitializrSpringCloudInfoService(RestTemplate rest, Github github, GithubPomReader reader) {
|
||||
super(github, reader);
|
||||
this.rest = rest;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("springCloudViaBoot")
|
||||
public SpringCloudVersion getSpringCloudVersion(String springBootVersion)
|
||||
throws SpringCloudVersionNotFoundException {
|
||||
Map<String, SpringBootAndCloudVersion> cache = new HashMap<>();
|
||||
Map<String, Object> response = rest.getForObject(INITIALIZR_URL, Map.class);
|
||||
if (!response.containsKey("bom-ranges")) {
|
||||
throw new SpringCloudVersionNotFoundException(
|
||||
new InitializrParseException("bom-ranges key not found in Initializr info endpoint"));
|
||||
}
|
||||
Map<String, Object> bomRanges = (Map<String, Object>) response.get("bom-ranges");
|
||||
if (!bomRanges.containsKey("spring-cloud")) {
|
||||
throw new SpringCloudVersionNotFoundException(
|
||||
new InitializrParseException("spring-cloud key not found in Initializr info endpoint"));
|
||||
}
|
||||
|
||||
Map<String, String> springCloud = (Map<String, String>) bomRanges.get("spring-cloud");
|
||||
for (String key : springCloud.keySet()) {
|
||||
String rangeString = springCloud.get(key);
|
||||
cache.put(key, parseRangeString(rangeString, key));
|
||||
}
|
||||
for (String key : cache.keySet()) {
|
||||
if (cache.get(key).matchesSpringBootVersion(springBootVersion)) {
|
||||
return new SpringCloudVersion(key);
|
||||
}
|
||||
}
|
||||
throw new SpringCloudVersionNotFoundException(springBootVersion);
|
||||
}
|
||||
|
||||
private SpringBootAndCloudVersion parseRangeString(String rangeString, String springCloudVersion) {
|
||||
// Example of rangeString Spring Boot >=2.0.0.M3 and <2.0.0.M5
|
||||
String versions = rangeString.substring(13);
|
||||
boolean startVersionInclusive = true;
|
||||
if (versions.charAt(0) == '=') {
|
||||
versions = versions.substring(1);
|
||||
|
||||
}
|
||||
else {
|
||||
startVersionInclusive = false;
|
||||
}
|
||||
// Example of versions 2.0.0.M3 and <2.0.0.M5 or 2.0.0.M3 and <=2.0.0.M5
|
||||
String[] cleanedVersions;
|
||||
boolean endVersionInclusive = true;
|
||||
if (versions.contains("=")) {
|
||||
cleanedVersions = versions.split(" and <=");
|
||||
}
|
||||
else {
|
||||
endVersionInclusive = false;
|
||||
cleanedVersions = versions.split(" and <");
|
||||
}
|
||||
if (cleanedVersions.length == 1) {
|
||||
return new SpringBootAndCloudVersion(cleanedVersions[0], startVersionInclusive, "99999.99999.99999.RELEASE",
|
||||
endVersionInclusive, springCloudVersion);
|
||||
}
|
||||
return new SpringBootAndCloudVersion(cleanedVersions[0], startVersionInclusive, cleanedVersions[1],
|
||||
endVersionInclusive, springCloudVersion);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class SpringBootAndCloudVersion {
|
||||
|
||||
private String bootStartVersion;
|
||||
|
||||
private ComparableVersion comparableBootStartVersion;
|
||||
|
||||
private boolean startVersionInclusive;
|
||||
|
||||
private String bootEndVersion;
|
||||
|
||||
private ComparableVersion comparableBootEndVersion;
|
||||
|
||||
private boolean endVersionInclusive;
|
||||
|
||||
private String springCloudVersion;
|
||||
|
||||
public SpringBootAndCloudVersion(String bootStartVersion, boolean statVersionInclusive, String bootEndVersion,
|
||||
boolean endVersionInclusive, String springCloudVersion) {
|
||||
this.bootEndVersion = bootEndVersion;
|
||||
this.comparableBootEndVersion = new ComparableVersion(bootEndVersion);
|
||||
this.endVersionInclusive = endVersionInclusive;
|
||||
this.bootStartVersion = bootStartVersion;
|
||||
this.comparableBootStartVersion = new ComparableVersion(bootStartVersion);
|
||||
this.startVersionInclusive = statVersionInclusive;
|
||||
this.springCloudVersion = springCloudVersion;
|
||||
}
|
||||
|
||||
String getBootStartVersion() {
|
||||
return bootStartVersion;
|
||||
}
|
||||
|
||||
void setBootStartVersion(String bootStartVersion) {
|
||||
this.bootStartVersion = bootStartVersion;
|
||||
}
|
||||
|
||||
String getBootEndVersion() {
|
||||
return bootEndVersion;
|
||||
}
|
||||
|
||||
void setBootEndVersion(String bootEndVersion) {
|
||||
this.bootEndVersion = bootEndVersion;
|
||||
}
|
||||
|
||||
String getSpringCloudVersion() {
|
||||
return springCloudVersion;
|
||||
}
|
||||
|
||||
void setSpringCloudVersion(String springCloudVersion) {
|
||||
this.springCloudVersion = springCloudVersion;
|
||||
}
|
||||
|
||||
boolean matchesSpringBootVersion(String versionToCheck) {
|
||||
return matchesSpringBootVersion(new ComparableVersion(versionToCheck));
|
||||
}
|
||||
|
||||
boolean matchesSpringBootVersion(ComparableVersion versionToCheck) {
|
||||
int startVersionComparison = comparableBootStartVersion.compareTo(versionToCheck);
|
||||
int endVersionComparison = versionToCheck.compareTo(comparableBootEndVersion);
|
||||
return ((startVersionInclusive && startVersionComparison == 0) || startVersionComparison <= -1)
|
||||
&& ((endVersionInclusive && endVersionComparison == 0) || endVersionComparison <= -1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.github.RtGithub;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableCaching
|
||||
@EnableConfigurationProperties({ SpringCloudInfoConfigurationProperties.class })
|
||||
public class SpringCloudInfoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringCloudInfoApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringCloudInfoService initializrSpringCloudVersionService(
|
||||
SpringCloudInfoConfigurationProperties properties) {
|
||||
Github github = new RtGithub(properties.getGit().getOauthToken());
|
||||
RestTemplate rest = new RestTemplateBuilder().build();
|
||||
return new InitializrSpringCloudInfoService(rest, github, new GithubPomReader(new MavenXpp3Reader(), rest));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests().anyRequest().permitAll().and().httpBasic().disable().csrf().disable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,39 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package releaser.internal.sagan;
|
||||
package org.springframework.cloud.info;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class ProjectDetails {
|
||||
@ConfigurationProperties("spring.cloud.info")
|
||||
public class SpringCloudInfoConfigurationProperties {
|
||||
|
||||
private String bootConfig;
|
||||
private Git git = new Git();
|
||||
|
||||
private String body;
|
||||
|
||||
public String getBootConfig() {
|
||||
return bootConfig;
|
||||
public Git getGit() {
|
||||
return git;
|
||||
}
|
||||
|
||||
public void setBootConfig(String bootConfig) {
|
||||
this.bootConfig = bootConfig;
|
||||
public void setGit(Git git) {
|
||||
this.git = git;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
public static class Git {
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
private String oauthToken = "";
|
||||
|
||||
public String getOauthToken() {
|
||||
return oauthToken;
|
||||
}
|
||||
|
||||
public void setOauthToken(String oauthToken) {
|
||||
this.oauthToken = oauthToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectDetails{" + "bootConfig='" + bootConfig + '\'' + ", body='" + body + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.info.SpringCloudInfoService.SpringCloudVersion;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudMilestoneNotFoundException;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudVersionNotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@RestController
|
||||
public class SpringCloudInfoRestController {
|
||||
|
||||
private SpringCloudInfoService versionService;
|
||||
|
||||
public SpringCloudInfoRestController(SpringCloudInfoService versionService) {
|
||||
this.versionService = versionService;
|
||||
}
|
||||
|
||||
@GetMapping("/springcloudversion/springboot/{bootVersion}")
|
||||
public SpringCloudVersion version(@PathVariable String bootVersion) {
|
||||
try {
|
||||
return versionService.getSpringCloudVersion(bootVersion);
|
||||
}
|
||||
catch (SpringCloudVersionNotFoundException e) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/springcloudversions")
|
||||
public Collection<String> versions() throws IOException {
|
||||
return versionService.getSpringCloudVersions();
|
||||
}
|
||||
|
||||
@GetMapping("/bomversions/{bomVersion}")
|
||||
public Map<String, String> bomVersions(@PathVariable String bomVersion) throws IOException {
|
||||
try {
|
||||
return versionService.getReleaseVersions(bomVersion);
|
||||
}
|
||||
catch (SpringCloudVersionNotFoundException e) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/milestones")
|
||||
public Collection<String> milestones() throws IOException {
|
||||
return versionService.getMilestones();
|
||||
}
|
||||
|
||||
@GetMapping("/milestones/{name}/duedate")
|
||||
public SpringCloudInfoService.Milestone milestoneDueDate(@PathVariable String name) throws IOException {
|
||||
try {
|
||||
return versionService.getMilestoneDueDate(name);
|
||||
}
|
||||
catch (SpringCloudMilestoneNotFoundException e) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudMilestoneNotFoundException;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudVersionNotFoundException;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public interface SpringCloudInfoService {
|
||||
|
||||
SpringCloudVersion getSpringCloudVersion(String bootVersion) throws SpringCloudVersionNotFoundException;
|
||||
|
||||
Collection<String> getSpringCloudVersions() throws IOException;
|
||||
|
||||
Map<String, String> getReleaseVersions(String bomVersion) throws SpringCloudVersionNotFoundException, IOException;
|
||||
|
||||
Collection<String> getMilestones() throws IOException;
|
||||
|
||||
Milestone getMilestoneDueDate(String name) throws SpringCloudMilestoneNotFoundException, IOException;
|
||||
|
||||
class Milestone {
|
||||
|
||||
private String dueDate;
|
||||
|
||||
public Milestone() {
|
||||
}
|
||||
|
||||
public Milestone(String dueDate) {
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
public String getDueDate() {
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
public void setDueDate(String dueDate) {
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Milestone milestone = (Milestone) o;
|
||||
return Objects.equals(getDueDate(), milestone.getDueDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getDueDate());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SpringCloudVersion {
|
||||
|
||||
private String version;
|
||||
|
||||
public SpringCloudVersion() {
|
||||
}
|
||||
|
||||
public SpringCloudVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SpringCloudVersion that = (SpringCloudVersion) o;
|
||||
return Objects.equals(getVersion(), that.getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getVersion());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.json.JsonArray;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
|
||||
import com.jcabi.github.Coordinates;
|
||||
import com.jcabi.github.Github;
|
||||
import com.jcabi.http.response.JsonResponse;
|
||||
import org.apache.maven.model.Model;
|
||||
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
|
||||
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudMilestoneNotFoundException;
|
||||
import org.springframework.cloud.info.exceptions.SpringCloudVersionNotFoundException;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public abstract class SpringCloudRelease implements SpringCloudInfoService {
|
||||
|
||||
/**
|
||||
* Coordinates to Spring Cloud Release in Github.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_RELEASE_COORDINATES = "spring-cloud/spring-cloud-release";
|
||||
|
||||
/**
|
||||
* Github tags path.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_RELEASE_TAGS_PATH = "repos/" + SPRING_CLOUD_RELEASE_COORDINATES + "/tags";
|
||||
|
||||
private static final String SPRING_CLOUD_RELEASE_RAW = "https://raw.githubusercontent.com/"
|
||||
+ SPRING_CLOUD_RELEASE_COORDINATES;
|
||||
|
||||
/**
|
||||
* URL to the raw spring-cloud-dependencies file on Github.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_RELEASE_DEPENDENCIES_RAW = SPRING_CLOUD_RELEASE_RAW
|
||||
+ "/%s/spring-cloud-dependencies/pom.xml";
|
||||
|
||||
/**
|
||||
* URL to the raw spring-cloud-starter-parent file on Github.
|
||||
*/
|
||||
public static final String SPRING_CLOUD_STARTER_PARENT_RAW = SPRING_CLOUD_RELEASE_RAW
|
||||
+ "/%s/spring-cloud-starter-parent/pom.xml";
|
||||
|
||||
private Github github;
|
||||
|
||||
private GithubPomReader reader;
|
||||
|
||||
public SpringCloudRelease(Github github, GithubPomReader reader) {
|
||||
this.github = github;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("springCloudVersions")
|
||||
public Collection<String> getSpringCloudVersions() throws IOException {
|
||||
List<String> releaseVersions = new ArrayList<>();
|
||||
JsonReader reader = github.entry().uri().path(SPRING_CLOUD_RELEASE_TAGS_PATH).back().fetch()
|
||||
.as(JsonResponse.class).json();
|
||||
JsonArray tags = reader.readArray();
|
||||
reader.close();
|
||||
List<JsonObject> tagsList = tags.getValuesAs(JsonObject.class);
|
||||
for (JsonObject obj : tagsList) {
|
||||
releaseVersions.add(obj.getString("name").replaceFirst("v", ""));
|
||||
}
|
||||
return releaseVersions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("releaseVersions")
|
||||
public Map<String, String> getReleaseVersions(String bomVersion)
|
||||
throws SpringCloudVersionNotFoundException, IOException {
|
||||
bomVersion = formatBomVersion(bomVersion);
|
||||
if (!getSpringCloudVersions().contains(bomVersion)) {
|
||||
throw new SpringCloudVersionNotFoundException();
|
||||
}
|
||||
try {
|
||||
Map<String, String> versions = new HashMap<>();
|
||||
Model model = reader.readPomFromUrl(String.format(SPRING_CLOUD_RELEASE_DEPENDENCIES_RAW, bomVersion));
|
||||
for (String name : model.getProperties().stringPropertyNames()) {
|
||||
if (name.startsWith("spring-cloud-")) {
|
||||
versions.put(name.replace(".version", ""), model.getProperties().getProperty(name));
|
||||
}
|
||||
}
|
||||
versions.put("spring-boot", getSpringCloudBootVersion(bomVersion));
|
||||
return versions;
|
||||
}
|
||||
catch (XmlPullParserException e) {
|
||||
throw new SpringCloudVersionNotFoundException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("milestones")
|
||||
public Collection<String> getMilestones() throws IOException {
|
||||
Set<String> milestones = new HashSet<>();
|
||||
Iterable<com.jcabi.github.Milestone> githubMilestones = getMilestonesFromGithub();
|
||||
for (com.jcabi.github.Milestone milestone : githubMilestones) {
|
||||
JsonObject json = milestone.json();
|
||||
milestones.add(json.getString("title"));
|
||||
}
|
||||
return milestones;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable("milestoneDueDate")
|
||||
public Milestone getMilestoneDueDate(String name) throws SpringCloudMilestoneNotFoundException, IOException {
|
||||
Iterable<com.jcabi.github.Milestone> milestones = getMilestonesFromGithub();
|
||||
for (com.jcabi.github.Milestone milestone : milestones) {
|
||||
JsonObject json = milestone.json();
|
||||
if (json.getString("title").equalsIgnoreCase(name)) {
|
||||
if (json.isNull("due_on")) {
|
||||
return new Milestone("No Due Date");
|
||||
}
|
||||
else {
|
||||
Instant instant = Instant.parse(json.getString("due_on"));
|
||||
return new Milestone(LocalDateTime.ofInstant(instant, ZoneId.of(ZoneOffset.UTC.getId()))
|
||||
.toLocalDate().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new SpringCloudMilestoneNotFoundException(name);
|
||||
}
|
||||
|
||||
private Iterable<com.jcabi.github.Milestone> getMilestonesFromGithub() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("state", "open");
|
||||
return github.repos().get(new Coordinates.Simple(SPRING_CLOUD_RELEASE_COORDINATES)).milestones()
|
||||
.iterate(params);
|
||||
}
|
||||
|
||||
private String getSpringCloudBootVersion(String bomVersion) throws IOException, XmlPullParserException {
|
||||
Model model = reader.readPomFromUrl(String.format(SPRING_CLOUD_STARTER_PARENT_RAW, bomVersion));
|
||||
return model.getParent().getVersion();
|
||||
}
|
||||
|
||||
private String formatBomVersion(String bomVersion) {
|
||||
if (bomVersion.charAt(0) != 'v') {
|
||||
return "v" + bomVersion;
|
||||
}
|
||||
else {
|
||||
return bomVersion;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info.exceptions;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class InitializrParseException extends Exception {
|
||||
|
||||
public InitializrParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info.exceptions;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class SpringCloudMilestoneNotFoundException extends Exception {
|
||||
|
||||
public SpringCloudMilestoneNotFoundException(String name) {
|
||||
super("Spring Cloud milestone " + name + " was not found in Spring Cloud Release");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.info.exceptions;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class SpringCloudVersionNotFoundException extends Exception {
|
||||
|
||||
public SpringCloudVersionNotFoundException(String springBootVersion) {
|
||||
super("Spring Cloud version not found for Spring Boot Version " + springBootVersion);
|
||||
}
|
||||
|
||||
public SpringCloudVersionNotFoundException(Exception e) {
|
||||
super("Spring Cloud version not found", e);
|
||||
}
|
||||
|
||||
public SpringCloudVersionNotFoundException() {
|
||||
super("Spring Cloud version not found");
|
||||
}
|
||||
|
||||
}
|
||||
5
spring-cloud-info/src/main/resources/application.yml
Normal file
5
spring-cloud-info/src/main/resources/application.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
#management:
|
||||
# endpoints:
|
||||
# web:
|
||||
# exposure:
|
||||
# include: "*"
|
||||
6
spring-cloud-info/src/main/resources/bootstrap.yml
Normal file
6
spring-cloud-info/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
spring:
|
||||
application:
|
||||
name: spring-cloud-info
|
||||
cloud:
|
||||
config:
|
||||
label: spring-cloud-info-config
|
||||
File diff suppressed because one or more lines are too long
BIN
spring-cloud-info/src/main/resources/static/docs/favicon.ico
Normal file
BIN
spring-cloud-info/src/main/resources/static/docs/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
2
spring-cloud-info/src/main/resources/static/docs/js/highlight/highlight.min.js
vendored
Normal file
2
spring-cloud-info/src/main/resources/static/docs/js/highlight/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
99
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/a11y-dark.min.css
vendored
Normal file
99
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/a11y-dark.min.css
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/* a11y-dark theme */
|
||||
/* Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css */
|
||||
/* @author: ericwbailey */
|
||||
|
||||
/* Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #d4d0ab;
|
||||
}
|
||||
|
||||
/* Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-regexp,
|
||||
.hljs-deletion {
|
||||
color: #ffa07a;
|
||||
}
|
||||
|
||||
/* Orange */
|
||||
.hljs-number,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params,
|
||||
.hljs-meta,
|
||||
.hljs-link {
|
||||
color: #f5ab35;
|
||||
}
|
||||
|
||||
/* Yellow */
|
||||
.hljs-attribute {
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
/* Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition {
|
||||
color: #abe338;
|
||||
}
|
||||
|
||||
/* Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #00e0e0;
|
||||
}
|
||||
|
||||
/* Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #dcc6e0;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #2b2b2b;
|
||||
color: #f8f8f2;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media screen and (-ms-high-contrast: active) {
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-bullet,
|
||||
.hljs-comment,
|
||||
.hljs-link,
|
||||
.hljs-literal,
|
||||
.hljs-meta,
|
||||
.hljs-number,
|
||||
.hljs-params,
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-type,
|
||||
.hljs-quote {
|
||||
color: highlight;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
89
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/an-old-hope.min.css
vendored
Normal file
89
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/an-old-hope.min.css
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
|
||||
An Old Hope – Star Wars Syntax (c) Gustavo Costa <gusbemacbe@gmail.com>
|
||||
Original theme - Ocean Dark Theme – by https://github.com/gavsiu
|
||||
Based on Jesse Leite's Atom syntax theme 'An Old Hope' – https://github.com/JesseLeite/an-old-hope-syntax-atom
|
||||
|
||||
*/
|
||||
|
||||
/* Death Star Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote
|
||||
{
|
||||
color: #B6B18B;
|
||||
}
|
||||
|
||||
/* Darth Vader */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-regexp,
|
||||
.hljs-deletion
|
||||
{
|
||||
color: #EB3C54;
|
||||
}
|
||||
|
||||
/* Threepio */
|
||||
.hljs-number,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params,
|
||||
.hljs-meta,
|
||||
.hljs-link
|
||||
{
|
||||
color: #E7CE56;
|
||||
}
|
||||
|
||||
/* Luke Skywalker */
|
||||
.hljs-attribute
|
||||
{
|
||||
color: #EE7C2B;
|
||||
}
|
||||
|
||||
/* Obi Wan Kenobi */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition
|
||||
{
|
||||
color: #4FB4D7;
|
||||
}
|
||||
|
||||
/* Yoda */
|
||||
.hljs-title,
|
||||
.hljs-section
|
||||
{
|
||||
color: #78BB65;
|
||||
}
|
||||
|
||||
/* Mace Windu */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag
|
||||
{
|
||||
color: #B45EA4;
|
||||
}
|
||||
|
||||
/* Millenium Falcon */
|
||||
.hljs
|
||||
{
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
background: #1C1D21;
|
||||
color: #c0c5ce;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.hljs-emphasis
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
|
||||
Atom One Dark With support for ReasonML by Gidi Morris, based off work by Daniel Gamage
|
||||
|
||||
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||
|
||||
*/
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
line-height: 1.3em;
|
||||
color: #abb2bf;
|
||||
background: #282c34;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.hljs-keyword, .hljs-operator {
|
||||
color: #F92672;
|
||||
}
|
||||
.hljs-pattern-match {
|
||||
color: #F92672;
|
||||
}
|
||||
.hljs-pattern-match .hljs-constructor {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-function {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-function .hljs-params {
|
||||
color: #A6E22E;
|
||||
}
|
||||
.hljs-function .hljs-params .hljs-typing {
|
||||
color: #FD971F;
|
||||
}
|
||||
.hljs-module-access .hljs-module {
|
||||
color: #7e57c2;
|
||||
}
|
||||
.hljs-constructor {
|
||||
color: #e2b93d;
|
||||
}
|
||||
.hljs-constructor .hljs-string {
|
||||
color: #9CCC65;
|
||||
}
|
||||
.hljs-comment, .hljs-quote {
|
||||
color: #b18eb1;
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-doctag, .hljs-formula {
|
||||
color: #c678dd;
|
||||
}
|
||||
.hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst {
|
||||
color: #e06c75;
|
||||
}
|
||||
.hljs-literal {
|
||||
color: #56b6c2;
|
||||
}
|
||||
.hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string {
|
||||
color: #98c379;
|
||||
}
|
||||
.hljs-built_in, .hljs-class .hljs-title {
|
||||
color: #e6c07b;
|
||||
}
|
||||
.hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-type, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-number {
|
||||
color: #d19a66;
|
||||
}
|
||||
.hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
96
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/atom-one-dark.min.css
vendored
Normal file
96
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/atom-one-dark.min.css
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Atom One Dark by Daniel Gamage
|
||||
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||
|
||||
base: #282c34
|
||||
mono-1: #abb2bf
|
||||
mono-2: #818896
|
||||
mono-3: #5c6370
|
||||
hue-1: #56b6c2
|
||||
hue-2: #61aeee
|
||||
hue-3: #c678dd
|
||||
hue-4: #98c379
|
||||
hue-5: #e06c75
|
||||
hue-5-2: #be5046
|
||||
hue-6: #d19a66
|
||||
hue-6-2: #e6c07b
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #abb2bf;
|
||||
background: #282c34;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #5c6370;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-formula {
|
||||
color: #c678dd;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-tag,
|
||||
.hljs-deletion,
|
||||
.hljs-subst {
|
||||
color: #e06c75;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #56b6c2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-regexp,
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-meta-string {
|
||||
color: #98c379;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-class .hljs-title {
|
||||
color: #e6c07b;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-number {
|
||||
color: #d19a66;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
.hljs-selector-id,
|
||||
.hljs-title {
|
||||
color: #61aeee;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
96
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/atom-one-light.min.css
vendored
Normal file
96
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/atom-one-light.min.css
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Atom One Light by Daniel Gamage
|
||||
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
|
||||
|
||||
base: #fafafa
|
||||
mono-1: #383a42
|
||||
mono-2: #686b77
|
||||
mono-3: #a0a1a7
|
||||
hue-1: #0184bb
|
||||
hue-2: #4078f2
|
||||
hue-3: #a626a4
|
||||
hue-4: #50a14f
|
||||
hue-5: #e45649
|
||||
hue-5-2: #c91243
|
||||
hue-6: #986801
|
||||
hue-6-2: #c18401
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #383a42;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #a0a1a7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-formula {
|
||||
color: #a626a4;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-tag,
|
||||
.hljs-deletion,
|
||||
.hljs-subst {
|
||||
color: #e45649;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #0184bb;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-regexp,
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-meta-string {
|
||||
color: #50a14f;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-class .hljs-title {
|
||||
color: #c18401;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-number {
|
||||
color: #986801;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
.hljs-selector-id,
|
||||
.hljs-title {
|
||||
color: #4078f2;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
76
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/dracula.min.css
vendored
Normal file
76
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/dracula.min.css
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
|
||||
Dracula Theme v1.2.0
|
||||
|
||||
https://github.com/zenorocha/dracula-theme
|
||||
|
||||
Copyright 2015, All rights reserved
|
||||
|
||||
Code licensed under the MIT license
|
||||
http://zenorocha.mit-license.org
|
||||
|
||||
@author Éverton Ribeiro <nuxlli@gmail.com>
|
||||
@author Zeno Rocha <hi@zenorocha.com>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #282a36;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-section,
|
||||
.hljs-link {
|
||||
color: #8be9fd;
|
||||
}
|
||||
|
||||
.hljs-function .hljs-keyword {
|
||||
color: #ff79c6;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-title,
|
||||
.hljs-name,
|
||||
.hljs-type,
|
||||
.hljs-attribute,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #6272a4;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-doctag,
|
||||
.hljs-type,
|
||||
.hljs-name,
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
99
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/github.min.css
vendored
Normal file
99
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/github.min.css
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #998;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-subst {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag .hljs-attr {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-doctag {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-id {
|
||||
color: #900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-type,
|
||||
.hljs-class .hljs-title {
|
||||
color: #458;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-attribute {
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-meta {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background: #dfd;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
83
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/monokai-sublime.min.css
vendored
Normal file
83
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/monokai-sublime.min.css
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
|
||||
Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #23241f;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-tag,
|
||||
.hljs-subst {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-strong,
|
||||
.hljs-emphasis {
|
||||
color: #a8a8a2;
|
||||
}
|
||||
|
||||
.hljs-bullet,
|
||||
.hljs-quote,
|
||||
.hljs-number,
|
||||
.hljs-regexp,
|
||||
.hljs-literal,
|
||||
.hljs-link {
|
||||
color: #ae81ff;
|
||||
}
|
||||
|
||||
.hljs-code,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-class {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-name,
|
||||
.hljs-attr {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-attribute {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-params,
|
||||
.hljs-class .hljs-title {
|
||||
color: #f8f8f2;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable {
|
||||
color: #e6db74;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
70
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/monokai.min.css
vendored
Normal file
70
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/monokai.min.css
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Monokai style - ported by Luigi Maselli - http://grigio.org
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #272822; color: #ddd;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-strong,
|
||||
.hljs-name {
|
||||
color: #f92672;
|
||||
}
|
||||
|
||||
.hljs-code {
|
||||
color: #66d9ef;
|
||||
}
|
||||
|
||||
.hljs-class .hljs-title {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hljs-attribute,
|
||||
.hljs-symbol,
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #bf79db;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-emphasis,
|
||||
.hljs-type,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-addition,
|
||||
.hljs-variable,
|
||||
.hljs-template-tag,
|
||||
.hljs-template-variable {
|
||||
color: #a6e22e;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-deletion,
|
||||
.hljs-meta {
|
||||
color: #75715e;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-type,
|
||||
.hljs-selector-id {
|
||||
font-weight: bold;
|
||||
}
|
||||
84
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/solarized-light.min.css
vendored
Normal file
84
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/solarized-light.min.css
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
|
||||
Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull <sourdrums@gmail.com>
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #fdf6e3;
|
||||
color: #657b83;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #93a1a1;
|
||||
}
|
||||
|
||||
/* Solarized Green */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-addition {
|
||||
color: #859900;
|
||||
}
|
||||
|
||||
/* Solarized Cyan */
|
||||
.hljs-number,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-meta-string,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-regexp {
|
||||
color: #2aa198;
|
||||
}
|
||||
|
||||
/* Solarized Blue */
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #268bd2;
|
||||
}
|
||||
|
||||
/* Solarized Yellow */
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-class .hljs-title,
|
||||
.hljs-type {
|
||||
color: #b58900;
|
||||
}
|
||||
|
||||
/* Solarized Orange */
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-meta,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-link {
|
||||
color: #cb4b16;
|
||||
}
|
||||
|
||||
/* Solarized Red */
|
||||
.hljs-built_in,
|
||||
.hljs-deletion {
|
||||
color: #dc322f;
|
||||
}
|
||||
|
||||
.hljs-formula {
|
||||
background: #eee8d5;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
80
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/zenburn.min.css
vendored
Normal file
80
spring-cloud-info/src/main/resources/static/docs/js/highlight/styles/zenburn.min.css
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
|
||||
Zenburn style from voldmar.ru (c) Vladimir Epifanov <voldmar@voldmar.ru>
|
||||
based on dark.css by Ivan Sagalaev
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: #3f3f3f;
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-tag {
|
||||
color: #e3ceab;
|
||||
}
|
||||
|
||||
.hljs-template-tag {
|
||||
color: #dcdcdc;
|
||||
}
|
||||
|
||||
.hljs-number {
|
||||
color: #8cd0d3;
|
||||
}
|
||||
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-attribute {
|
||||
color: #efdcbc;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #efefaf;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
color: #8f8f8f;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-section,
|
||||
.hljs-type {
|
||||
color: #efef8f;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link {
|
||||
color: #dca3a3;
|
||||
}
|
||||
|
||||
.hljs-deletion,
|
||||
.hljs-string,
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #cc9393;
|
||||
}
|
||||
|
||||
.hljs-addition,
|
||||
.hljs-comment,
|
||||
.hljs-quote,
|
||||
.hljs-meta {
|
||||
color: #7f9f7f;
|
||||
}
|
||||
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
107
spring-cloud-info/src/main/resources/static/docs/js/toc.js
Normal file
107
spring-cloud-info/src/main/resources/static/docs/js/toc.js
Normal file
@@ -0,0 +1,107 @@
|
||||
var toctitle = document.getElementById('toctitle');
|
||||
var path = window.location.pathname;
|
||||
if (toctitle != null) {
|
||||
var oldtoc = toctitle.nextElementSibling;
|
||||
var newtoc = document.createElement('div');
|
||||
newtoc.setAttribute('id', 'tocbot');
|
||||
newtoc.setAttribute('class', 'js-toc desktop-toc');
|
||||
oldtoc.setAttribute('class', 'mobile-toc');
|
||||
oldtoc.parentNode.appendChild(newtoc);
|
||||
tocbot.init({
|
||||
contentSelector: '#content',
|
||||
headingSelector: 'h1, h2, h3, h4, h5',
|
||||
positionFixedSelector: 'body',
|
||||
fixedSidebarOffset: 90,
|
||||
smoothScroll: false
|
||||
});
|
||||
if (!path.endsWith("index.html") && !path.endsWith("/")) {
|
||||
var link = document.createElement("a");
|
||||
link.setAttribute("href", "index.html");
|
||||
link.innerHTML = "<span><i class=\"fa fa-chevron-left\" aria-hidden=\"true\"></i></span> Back to index";
|
||||
var block = document.createElement("div");
|
||||
block.setAttribute('class', 'back-action');
|
||||
block.appendChild(link);
|
||||
var toc = document.getElementById('toc');
|
||||
var next = document.getElementById('toctitle').nextElementSibling;
|
||||
toc.insertBefore(block, next);
|
||||
}
|
||||
}
|
||||
|
||||
var headerHtml = '<div id="header-spring">\n' +
|
||||
'<h1>\n' +
|
||||
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0"\n' +
|
||||
'viewBox="0 0 245.8 45.3" style="enable-background:new 0 0 245.8 45.3;" xml:space="preserve">\n' +
|
||||
'<g id="logos">\n' +
|
||||
'<g>\n' +
|
||||
'<path class="st0" d="M39.4,3.7c-0.6,1.5-1.4,2.8-2.3,4c-3.9-4-9.3-6.4-15.2-6.4c-11.7,0-21.3,9.5-21.3,21.3\n' +
|
||||
'c0,6.2,2.6,11.7,6.8,15.6l0.8,0.7c3.7,3.1,8.5,5,13.7,5c11.2,0,20.4-8.7,21.2-19.8C43.7,18.7,42.1,11.8,39.4,3.7z M10.5,38.3\n' +
|
||||
'c-0.6,0.8-1.8,0.9-2.6,0.3C7.1,37.9,7,36.8,7.6,36c0.6-0.8,1.8-0.9,2.6-0.3C11,36.4,11.1,37.5,10.5,38.3z M39.3,31.9\n' +
|
||||
'c-5.2,7-16.5,4.6-23.6,5c0,0-1.3,0.1-2.6,0.3c0,0,0.5-0.2,1.1-0.4c5-1.7,7.4-2.1,10.5-3.7c5.8-3,11.5-9.4,12.7-16.1\n' +
|
||||
'c-2.2,6.4-8.9,12-14.9,14.2c-4.2,1.5-11.7,3-11.7,3c0,0-0.3-0.2-0.3-0.2c-5.1-2.5-5.3-13.6,4-17.1c4.1-1.6,8-0.7,12.4-1.8\n' +
|
||||
'C31.6,14.1,37,10.6,39.2,6C41.7,13.3,44.7,24.8,39.3,31.9z"/>\n' +
|
||||
'<g>\n' +
|
||||
'<path class="st0" d="M55.2,30.9c-0.5-0.3-0.9-0.9-0.9-1.6c0-1.1,0.8-1.9,1.9-1.9c0.4,0,0.7,0.1,1,0.3c2,1.3,4.1,2,5.9,2\n' +
|
||||
'c2,0,3.2-0.9,3.2-2.2v-0.1c0-1.6-2.2-2.2-4.6-2.9c-3-0.9-6.5-2.1-6.5-6.1v-0.1c0-3.9,3.2-6.3,7.4-6.3c2.2,0,4.5,0.6,6.5,1.7\n' +
|
||||
'c0.7,0.4,1.1,1,1.1,1.8c0,1.1-0.9,1.9-2,1.9c-0.4,0-0.6-0.1-0.9-0.2c-1.7-0.9-3.4-1.4-4.9-1.4c-1.8,0-2.9,0.9-2.9,2v0.1\n' +
|
||||
'c0,1.5,2.2,2.2,4.7,2.9c3,0.9,6.4,2.3,6.4,6v0.1c0,4.3-3.4,6.5-7.7,6.5C60.4,33.3,57.6,32.5,55.2,30.9z"/>\n' +
|
||||
'<path class="st0" d="M72.5,14.3c0-1.3,1-2.4,2.3-2.4c1.3,0,2.4,1.1,2.4,2.4v1.4c1.5-2.2,3.7-3.9,7-3.9c4.8,0,9.6,3.8,9.6,10.7\n' +
|
||||
'v0.1c0,6.8-4.7,10.7-9.6,10.7c-3.4,0-5.6-1.7-7-3.6V37c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.3-1-2.3-2.4V14.3z M89.1,22.7L89.1,22.7\n' +
|
||||
'c0-4.1-2.7-6.7-5.9-6.7c-3.2,0-6,2.7-6,6.6v0.1c0,4,2.8,6.6,6,6.6C86.4,29.3,89.1,26.7,89.1,22.7z"/>\n' +
|
||||
'<path class="st0" d="M95.7,14.3c0-1.3,1-2.4,2.3-2.4c1.3,0,2.4,1.1,2.4,2.4v1.1c0.2-1.8,3.1-3.5,5.2-3.5c1.5,0,2.3,1,2.3,2.3\n' +
|
||||
'c0,1.3-0.8,2.1-1.9,2.3c-3.4,0.6-5.7,3.5-5.7,7.6V31c0,1.3-1.1,2.3-2.4,2.3c-1.3,0-2.3-1-2.3-2.3V14.3z"/>\n' +
|
||||
'<path class="st0" d="M109.7,14.3c0-1.3,1-2.4,2.3-2.4c1.3,0,2.4,1.1,2.4,2.4V31c0,1.3-1.1,2.3-2.4,2.3c-1.3,0-2.3-1-2.3-2.3V14.3\n' +
|
||||
'z"/>\n' +
|
||||
'<path class="st0" d="M116.9,14.3c0-1.3,1-2.4,2.3-2.4c1.3,0,2.4,1.1,2.4,2.4v1c1.3-1.9,3.2-3.4,6.5-3.4c4.7,0,7.4,3.1,7.4,7.9V31\n' +
|
||||
'c0,1.3-1,2.3-2.3,2.3c-1.3,0-2.4-1-2.4-2.3v-9.7c0-3.2-1.6-5-4.4-5c-2.7,0-4.7,1.9-4.7,5.1V31c0,1.3-1.1,2.3-2.4,2.3\n' +
|
||||
'c-1.3,0-2.3-1-2.3-2.3V14.3z"/>\n' +
|
||||
'<path class="st0" d="M156.2,11.9c-1.3,0-2.4,1.1-2.4,2.4v1.4c-1.5-2.2-3.7-3.9-7-3.9c-4.9,0-9.6,3.8-9.6,10.7v0.1\n' +
|
||||
'c0,6.8,4.7,10.7,9.6,10.7c3.4,0,5.6-1.7,7-3.6c-0.2,3.7-2.5,5.7-6.5,5.7c-2.4,0-4.5-0.6-6.3-1.6c-0.2-0.1-0.5-0.2-0.9-0.2\n' +
|
||||
'c-1.1,0-2,0.9-2,2c0,0.9,0.5,1.6,1.3,1.9c2.5,1.2,5.1,1.8,8,1.8c3.7,0,6.6-0.9,8.5-2.8c1.7-1.7,2.7-4.3,2.7-7.8V14.3\n' +
|
||||
'C158.5,13,157.5,11.9,156.2,11.9z M147.9,29.2c-3.2,0-5.9-2.5-5.9-6.6v-0.1c0-4,2.7-6.6,5.9-6.6c3.2,0,6,2.7,6,6.6v0.1\n' +
|
||||
'C153.9,26.6,151.1,29.2,147.9,29.2z"/>\n' +
|
||||
'<path class="st0" d="M114.5,6.3c0,1.3-1.1,2.4-2.4,2.4c-1.3,0-2.4-1.1-2.4-2.4c0-1.3,1.1-2.4,2.4-2.4\n' +
|
||||
'C113.4,3.9,114.5,4.9,114.5,6.3z"/>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'<g class="st1">\n' +
|
||||
'<g>\n' +
|
||||
'<g>\n' +
|
||||
'<g>\n' +
|
||||
'<path class="st2" d="M200.1,21.1H198V19h2.1V21.1z M200.1,32.9H198V22.6h2.1V32.9z"/>\n' +
|
||||
'</g>\n' +
|
||||
'<g>\n' +
|
||||
'<g>\n' +
|
||||
'<path class="st2" d="M212.5,22.6l-3,8.9c-0.5,1.5-1.4,1.6-2.2,1.6c-1.1,0-1.8-0.5-2.2-1.6l-2.5-7.4h-1v-1.5h2.6l2.6,8.3\n' +
|
||||
'c0.1,0.4,0.2,0.6,0.5,0.6c0.3,0,0.4-0.2,0.5-0.6l2.6-8.3H212.5z"/>\n' +
|
||||
'<path class="st2" d="M217.8,22.6c2.8,0,4.7,1.8,4.7,4.5v1.6c0,2.6-1.9,4.5-4.7,4.5c-2.8,0-4.7-1.8-4.7-4.5v-1.6\n' +
|
||||
'C213,24.4,215,22.6,217.8,22.6 M217.8,31.4c1.7,0,2.7-1.3,2.7-2.8v-1.6c0-1.5-1-2.8-2.7-2.8c-1.8,0-2.7,1.3-2.7,2.8v1.6\n' +
|
||||
'C215.1,30.2,216,31.4,217.8,31.4"/>\n' +
|
||||
'<path class="st2" d="M239.6,22.9c-1.1-0.3-2.7-0.5-4-0.5c-2.8,0-4.6,1.8-4.6,4.6v1.1c0,2.9,1.7,4.7,4.6,4.7c0.1,0,0.6,0,0.8,0\n' +
|
||||
'v-1.7c-0.1,0-0.7,0-0.8,0c-1.5,0-2.6-1.2-2.6-2.9v-1.1c0-1.8,1-2.9,2.6-2.9c0.7,0,1.7,0.1,2.1,0.1l0.1,0l0,8.6h2.1v-9.6\n' +
|
||||
'C240,23.1,240,23,239.6,22.9"/>\n' +
|
||||
'<rect x="242.1" y="19" class="st2" width="2.1" height="13.9"/>\n' +
|
||||
'<path class="st2" d="M190.5,19h-3.8v13.9h2.2V20.9h1.3c0.3,0,0.5,0,0.8,0c1.9,0,2.9,0.8,2.9,2.3c0,0.1,0,0.1,0,0.2\n' +
|
||||
'c0,1.4-0.8,2.3-2.9,2.3c-0.2,0-0.4,0-0.6,0c0,0.5,0,1.5,0,1.9c0.2,0,0.4,0,0.6,0c3,0,5.2-1.2,5.2-4.2c0-0.1,0-0.1,0-0.2\n' +
|
||||
'C196.2,20.2,193.9,19,190.5,19"/>\n' +
|
||||
'<path class="st2" d="M226.3,20.4v2.2h3.5v1.7h-3.5v6c0,0.9,0.6,1,1.5,1h2v1.7H227c-2,0-2.9-0.8-2.9-2.6v-9.6L226.3,20.4z"/>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'<g>\n' +
|
||||
'<path class="st2" d="M167.7,32.9v-10h1.1v3.8c0.6-0.8,1.5-1.3,2.4-1.3c1.9,0,3.2,1.5,3.2,3.8c0,2.4-1.3,3.8-3.2,3.8\n' +
|
||||
'c-1,0-1.9-0.5-2.4-1.3v1.1H167.7z M171,32.1c1.5,0,2.3-1.2,2.3-2.8c0-1.6-0.9-2.8-2.3-2.8c-0.9,0-1.8,0.5-2.2,1.2v3.3\n' +
|
||||
'C169.2,31.6,170.1,32.1,171,32.1z"/>\n' +
|
||||
'<path class="st2" d="M175.9,34.7c0.2,0.1,0.4,0.1,0.6,0.1c0.5,0,0.8-0.2,1.1-0.8l0.5-1.1l-3-7.3h1.2l2.4,5.9l2.4-5.9h1.2\n' +
|
||||
'l-3.6,8.7c-0.4,1-1.2,1.5-2.1,1.5c-0.2,0-0.6,0-0.8-0.1L175.9,34.7z"/>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'</g>\n' +
|
||||
'</svg>\n' +
|
||||
'\n' +
|
||||
'</h1>\n' +
|
||||
'</div>';
|
||||
|
||||
var header = document.createElement("div");
|
||||
header.innerHTML = headerHtml;
|
||||
document.body.insertBefore(header, document.body.firstChild);
|
||||
@@ -0,0 +1 @@
|
||||
.toc{overflow-y:auto}.toc>.toc-list{overflow:hidden;position:relative}.toc>.toc-list li{list-style:none}.toc-list{margin:0;padding-left:10px}a.toc-link{color:currentColor;height:100%}.is-collapsible{max-height:1000px;overflow:hidden;transition:all 300ms ease-in-out}.is-collapsed{max-height:0}.is-position-fixed{position:fixed !important;top:0}.is-active-link{font-weight:700}.toc-link::before{background-color:#EEE;content:' ';display:inline-block;height:inherit;left:0;margin-top:-1px;position:absolute;width:2px}.is-active-link::before{background-color:#54BC4B}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user