From 6e658725f63810830ed62a9f032ad56cf4eb75b6 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 9 Jun 2020 16:39:15 +0200 Subject: [PATCH] Release train documentation rendering --- .mvn/wrapper/MavenWrapperDownloader.java | 51 +-- .mvn/wrapper/maven-wrapper.jar | Bin 48337 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 3 +- README.adoc | 144 +++++++- docs/pom.xml | 21 +- mvnw | 36 +- mvnw.cmd | 343 ++++++++++-------- pom.xml | 15 + train-docs/pom.xml | 131 +++++++ .../cloud/internal/ArtifactFetcher.java | 144 ++++++++ .../ConfigurationPropertiesAggregator.java | 62 ++++ .../cloud/internal/ConfigurationProperty.java | 66 ++++ .../internal/GenerateReleaseTrainDocs.java | 118 ++++++ .../cloud/internal/HandlebarsHelper.java | 48 +++ .../cloud/internal/Logger.java | 33 ++ .../cloud/internal/PomReader.java | 68 ++++ .../cloud/internal/Project.java | 64 ++++ .../cloud/internal/ResourcesCopier.java | 44 +++ .../cloud/internal/TemplateGenerator.java | 103 ++++++ .../cloud/internal/TemplateProject.java | 65 ++++ .../cloud/internal/ZipCategory.java | 98 +++++ .../templates/spring-cloud/_attributes.hbs | 15 + .../spring-cloud/_spring-cloud-attributes.hbs | 2 + .../spring-cloud/_spring-cloud-links.hbs | 2 + .../spring-cloud/_spring-cloud-versions.hbs | 2 + .../templates/spring-cloud/configprops.hbs | 7 + .../spring-cloud/documentation-overview.hbs | 38 ++ .../templates/spring-cloud/legal.hbs | 11 + .../spring-cloud/spring-cloud-pdf.hbs | 57 +++ .../spring-cloud/spring-cloud-single.hbs | 57 +++ .../templates/spring-cloud/spring-cloud.hbs | 44 +++ .../GenerateReleaseTrainDocsTests.java | 111 ++++++ ...heFinalReleaseTrainDocumentationTests.java | 52 +++ train-docs/src/test/resources/logback.xml | 25 ++ train-docs/src/test/resources/test/pom.xml | 255 +++++++++++++ .../src/test/resources/test/sleuth-only.xml | 236 ++++++++++++ .../src/test/resources/test/starter-pom.xml | 156 ++++++++ 37 files changed, 2517 insertions(+), 210 deletions(-) mode change 100755 => 100644 .mvn/wrapper/MavenWrapperDownloader.java mode change 100755 => 100644 .mvn/wrapper/maven-wrapper.jar mode change 100755 => 100644 .mvn/wrapper/maven-wrapper.properties mode change 100755 => 100644 mvnw.cmd create mode 100644 train-docs/pom.xml create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/ArtifactFetcher.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationPropertiesAggregator.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationProperty.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/GenerateReleaseTrainDocs.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/HandlebarsHelper.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/Logger.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/PomReader.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/Project.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/ResourcesCopier.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/TemplateGenerator.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/TemplateProject.java create mode 100644 train-docs/src/main/java/org/springframework/cloud/internal/ZipCategory.java create mode 100644 train-docs/src/main/resources/templates/spring-cloud/_attributes.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-attributes.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-links.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-versions.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/configprops.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/documentation-overview.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/legal.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/spring-cloud-pdf.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/spring-cloud-single.hbs create mode 100644 train-docs/src/main/resources/templates/spring-cloud/spring-cloud.hbs create mode 100644 train-docs/src/test/java/org/springframework/cloud/internal/GenerateReleaseTrainDocsTests.java create mode 100644 train-docs/src/test/java/org/springframework/cloud/internal/TestThatGeneratesTheFinalReleaseTrainDocumentationTests.java create mode 100644 train-docs/src/test/resources/logback.xml create mode 100644 train-docs/src/test/resources/test/pom.xml create mode 100644 train-docs/src/test/resources/test/sleuth-only.xml create mode 100644 train-docs/src/test/resources/test/starter-pom.xml diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java old mode 100755 new mode 100644 index 2e394d5..b901097 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -1,22 +1,18 @@ /* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you 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. -*/ - + * Copyright 2007-present 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 + * + * http://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. + */ import java.net.*; import java.io.*; import java.nio.channels.*; @@ -24,11 +20,12 @@ import java.util.Properties; public class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"; + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to @@ -76,13 +73,13 @@ public class MavenWrapperDownloader { } } } - System.out.println("- Downloading from: : " + url); + System.out.println("- Downloading from: " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); @@ -98,6 +95,16 @@ public class MavenWrapperDownloader { } private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar old mode 100755 new mode 100644 index 01e67997377a393fd672c7dcde9dccbedf0cb1e9..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch delta 12928 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 +    └── main +    └── resources +    ├── checkstyle-header.txt <2> +    └── checkstyle.xml <1> +---- +<1> Default Checkstyle rules +<2> File header setup +<3> Default suppression rules + +==== Checkstyle configuration + +Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins. + +.pom.xml +---- + +true <1> + true + <2> + true + <3> + + + + + <4> + io.spring.javaformat + spring-javaformat-maven-plugin + + <5> + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + <5> + org.apache.maven.plugins + maven-checkstyle-plugin + + + + +---- +<1> Fails the build upon Checkstyle errors +<2> Fails the build upon Checkstyle violations +<3> Checkstyle analyzes also the test sources +<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules +<5> Add checkstyle plugin to your build and reporting phases + +If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example: + +.projectRoot/src/checkstyle/checkstyle-suppresions.xml +---- + + + + + + +---- + +It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script: + +```bash +$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig +$ touch .springformat +``` + +=== IDE setup + +==== Intellij IDEA + +In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin. +The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project. + +.spring-cloud-build-tools/ +---- +└── src +    ├── checkstyle +    │   └── checkstyle-suppressions.xml <3> +    └── main +    └── resources +    ├── checkstyle-header.txt <2> +    ├── checkstyle.xml <1> +    └── intellij +       ├── Intellij_Project_Defaults.xml <4> +       └── Intellij_Spring_Boot_Java_Conventions.xml <5> +---- +<1> Default Checkstyle rules +<2> File header setup +<3> Default suppression rules +<4> Project defaults for Intellij that apply most of Checkstyle rules +<5> Project style conventions for Intellij that apply most of Checkstyle rules + +.Code style + +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style] + +Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file. + +.Inspection profiles + +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style] + +Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file. + +.Checkstyle + +To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions + +image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle] + +Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables: + +- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL. +- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL. +- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`. + +IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources. + == Building and Deploying Since there is no code to compile in the starters they should do not need to compile, but a compiler has to be available because they are built and deployed as JAR artifacts. To install locally: @@ -82,4 +222,4 @@ and for Maven Central use $ mvn install -P central -DaltReleaseDeploymentRepository=sonatype-nexus-staging::default::https://oss.sonatype.org/service/local/staging/deploy/maven2 ---- -(the "central" profile is available for all projects in Spring Cloud and it sets up the gpg jar signing, and the repository has to be specified separately for this project because it is a parent of the starter parent which users in turn have as their own parent). \ No newline at end of file +(the "central" profile is available for all projects in Spring Cloud and it sets up the gpg jar signing, and the repository has to be specified separately for this project because it is a parent of the starter parent which users in turn have as their own parent). diff --git a/docs/pom.xml b/docs/pom.xml index f7973c5..11d9298 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -2,7 +2,6 @@ 4.0.0 - org.springframework.cloud spring-cloud-starter-docs org.springframework.cloud @@ -15,33 +14,21 @@ spring-cloud-starters ${basedir}/.. - Greenwich,Hoxton + deploy - - - - maven-deploy-plugin - - true - - - - docs - + pl.project13.maven git-commit-id-plugin - org.apache.maven.plugins maven-dependency-plugin - org.apache.maven.plugins maven-resources-plugin @@ -53,12 +40,10 @@ asciidoctor-maven-plugin - org.apache.maven.plugins maven-antrun-plugin - org.codehaus.mojo - build-helper-maven-plugin + maven-deploy-plugin diff --git a/mvnw b/mvnw index 8b9da3b..41c0f0c 100755 --- a/mvnw +++ b/mvnw @@ -8,7 +8,7 @@ # "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 +# http://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 @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -114,7 +114,6 @@ if $mingw ; then M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -212,7 +211,11 @@ else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac @@ -221,22 +224,38 @@ else echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi - wget "$jarUrl" -O "$wrapperJarPath" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi - curl -o "$wrapperJarPath" "$jarUrl" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then @@ -277,6 +296,11 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ diff --git a/mvnw.cmd b/mvnw.cmd old mode 100755 new mode 100644 index a5284c7..8611571 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,161 +1,182 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% -) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% -) -@REM End of extension - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index fe31b51..013f7a6 100644 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,21 @@ + + all + + spring-cloud-dependencies + spring-cloud-starter-parent + docs + train-docs + + + + train-docs + + train-docs + + spring diff --git a/train-docs/pom.xml b/train-docs/pom.xml new file mode 100644 index 0000000..2036d41 --- /dev/null +++ b/train-docs/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + spring-cloud-docs + + org.springframework.cloud + spring-cloud-starter-build + 2020.0.0-SNAPSHOT + + jar + Spring Cloud Release Train Docs + Spring Cloud Release Train Docs + + spring-cloud + ${basedir}/.. + deploy + + 3.6.3 + + 1.4.1 + 4.0.6 + 2.1.0 + 2.7 + + + + org.springframework + spring-core + + + org.apache.maven + maven-model + ${maven.version} + + + com.github.jknack + handlebars + ${handlebars.version} + + + commons-io + commons-io + ${commons-io.version} + + + + org.springframework.cloud + spring-cloud-starter-docs + ${project.version} + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + docs + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${maven.multiModuleProjectDirectory}/spring-cloud-dependencies/pom.xml + ${maven.multiModuleProjectDirectory}/spring-cloud-starter-parent/pom.xml + ${maven-deploy-plugin.deployZipUrl} + ${project.build.directory}/unpacked-docs/ + ${project.build.directory}/train-docs/ + + + + + pl.project13.maven + git-commit-id-plugin + + + maven-dependency-plugin + + + maven-resources-plugin + + + org.codehaus.mojo + exec-maven-plugin + + + run-template-generation + prepare-package + + java + + + true + org.springframework.cloud.internal.GenerateReleaseTrainDocs + + ${maven.multiModuleProjectDirectory}/spring-cloud-dependencies/pom.xml + ${maven.multiModuleProjectDirectory}/spring-cloud-starter-parent/pom.xml + https://github.com/spring-cloud/ + ${project.build.directory}/unpacked-docs/ + ${project.build.directory}/train-docs/ + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + + ${project.build.directory}/train-docs/ + + ${project.build.directory}/unpacked-docs + + + + + maven-antrun-plugin + + + maven-deploy-plugin + + + + + + diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/ArtifactFetcher.java b/train-docs/src/main/java/org/springframework/cloud/internal/ArtifactFetcher.java new file mode 100644 index 0000000..3cb06d1 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/ArtifactFetcher.java @@ -0,0 +1,144 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.io.FileUtils; + +import org.springframework.util.FileSystemUtils; + +import static org.springframework.cloud.internal.Logger.error; +import static org.springframework.cloud.internal.Logger.info; + +class ArtifactFetcher { + + private static final int CONNECT_TIMEOUT = 5000; + + private static final int READ_TIMEOUT = 5000; + + private final File outputFolder; + + private final String repoUrl; + + ArtifactFetcher(File outputFolder, String repoUrl) { + this.outputFolder = outputFolder; + this.repoUrl = repoUrl; + } + + File unpackedDocs(Project project) { + String projectName = project.name; + String version = project.version; + List urls = urls(projectName, version); + final File downloadedZipsFolder = new File(outputFolder, "downloaded-zips"); + downloadedZipsFolder.mkdirs(); + File outputZip = new File(downloadedZipsFolder, projectName + ".zip"); + for (String url : urls) { + try { + info(projectName + ": Fetching sources from [" + url + + "]. Please wait..."); + FileUtils.copyURLToFile(new URL(url), outputZip, CONNECT_TIMEOUT, + READ_TIMEOUT); + info(projectName + ": Successfully fetched a zip from [" + url + "] to [" + + outputZip + "]"); + break; + } + catch (IOException ex) { + error(projectName + ": Failed to fetch a zip from [" + url + "]"); + } + } + if (!outputZip.exists()) { + error(projectName + + ": Exception occurred while trying to download an artifact with name [" + + projectName + "] and version [" + version + "]"); + return null; + } + return unpackDocs(projectName, outputZip); + } + + private File unpackDocs(String projectName, File outputZip) { + File unpackedDoc = unpackDoc(projectName, outputZip); + info(projectName + ": Unpacked file to [" + unpackedDoc.getAbsolutePath() + "]"); + if (unpackedDoc.isDirectory()) { + String[] subfolders = unpackedDoc.list(); + if (subfolders == null || subfolders.length != 1) { + return unpackedDoc; + } + moveOneFolderUp(unpackedDoc, subfolders[0]); + } + return unpackedDoc; + } + + private void moveOneFolderUp(File unpackedDoc, String subfolder) { + File onlySubfolder = new File(unpackedDoc, subfolder); + try { + FileSystemUtils.copyRecursively(onlySubfolder, unpackedDoc); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + FileSystemUtils.deleteRecursively(onlySubfolder); + } + + private List urls(String projectName, String version) { + ReleaseType releaseType = ReleaseType.fromVersion(version); + String sourcesUrl = this.repoUrl + projectName + "/archive/"; + List sourcesUrls = new LinkedList<>(); + if (releaseType == ReleaseType.SNAPSHOT) { + // 2.0.0-SNAPSHOT or 2.0.0-BUILD-SNAPSHOT -> 2.0.x + String[] splitVersion = version.split("\\."); + sourcesUrls + .add(sourcesUrl + splitVersion[0] + "." + splitVersion[1] + ".x.zip"); + // fallback + sourcesUrls.add(sourcesUrl + "master.zip"); + } + else { + sourcesUrls.add(sourcesUrl + "v" + version + ".zip"); + } + return sourcesUrls; + } + + private File unpackDoc(String artifactName, File zip) { + File unzippedDocs = new File(outputFolder, artifactName); + unzippedDocs.mkdirs(); + info(artifactName + ": Unpacking from [" + zip + "]"); + ZipCategory.unzipTo(zip, unzippedDocs); + return unzippedDocs; + } + +} + +enum ReleaseType { + + SNAPSHOT, NON_SNAPSHOT; + + static ReleaseType fromVersion(String version) { + if (isSnapshot(version)) { + return ReleaseType.SNAPSHOT; + } + return ReleaseType.NON_SNAPSHOT; + } + + private static boolean isSnapshot(String version) { + return version.contains("SNAPSHOT"); + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationPropertiesAggregator.java b/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationPropertiesAggregator.java new file mode 100644 index 0000000..b4f3b8a --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationPropertiesAggregator.java @@ -0,0 +1,62 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.util.StringUtils; + +class ConfigurationPropertiesAggregator { + + private static final List wordsToIgnore = Arrays.asList("|===", + "|Name | Default | Description"); + + List mergedConfigurationProperties(Path unpackedDocs) { + try { + return Files.walk(unpackedDocs) + .filter(path -> path.endsWith("_configprops.adoc")).flatMap(path -> { + try { + return Files.readAllLines(path).stream() + .filter(s -> !StringUtils.isEmpty(s) + && !wordsToIgnore.contains(s)) + .map(s -> { + // |foo|bar|baz -> foo|bar|baz -> split -> + // foo,bar,baz + String[] strings = s.substring(1).split("\\|"); + return new ConfigurationProperty( + strings[0].trim(), strings[1].trim(), + strings[2].trim()); + }); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + }).sorted(Comparator.comparing(o -> o.name)) + .collect(Collectors.toList()); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationProperty.java b/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationProperty.java new file mode 100644 index 0000000..0ac6171 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/ConfigurationProperty.java @@ -0,0 +1,66 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.util.Objects; + +class ConfigurationProperty { + + final String name; + + final String defaultValue; + + final String description; + + ConfigurationProperty(String name, String defaultValue, String description) { + this.name = name; + this.defaultValue = defaultValue; + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigurationProperty that = (ConfigurationProperty) o; + return Objects.equals(name, that.name) + && Objects.equals(description, that.description) + && Objects.equals(defaultValue, that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, defaultValue); + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getDefaultValue() { + return defaultValue; + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/GenerateReleaseTrainDocs.java b/train-docs/src/main/java/org/springframework/cloud/internal/GenerateReleaseTrainDocs.java new file mode 100644 index 0000000..b204b07 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/GenerateReleaseTrainDocs.java @@ -0,0 +1,118 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.apache.maven.model.Model; + +import static org.springframework.cloud.internal.Logger.info; + +public class GenerateReleaseTrainDocs { + + final ExecutorService service; + + public static void main(String... args) { + File bomPath = new File(args[0]); + File starterParentPath = new File(args[1]); + String repoUrl = args[2]; + File unzippedDocs = new File(args[3]); + File generatedTrainDocs = new File(args[4]); + new GenerateReleaseTrainDocs().generate(bomPath, starterParentPath, repoUrl, + unzippedDocs, generatedTrainDocs); + } + + GenerateReleaseTrainDocs() { + this.service = Executors.newFixedThreadPool(4); + } + + void generate(File bomPath, File starterParentPath, String repoUrl, File unzippedDocs, + File generatedTrainDocs) { + List projects = mavenPropertiesToDocsProjects(bomPath); + info("Found the following projects [" + projects + "]"); + List outputFolders = downloadSources(unzippedDocs, projects, repoUrl); + projects.add(springBootVersion(starterParentPath)); + projects.sort(Comparator.comparing(o -> o.name)); + List configurationProperties = mergeConfigurationProperties( + unzippedDocs); + File file = renderAsciidocTemplates(generatedTrainDocs, projects, + configurationProperties); + info("Rendered docs templates to [" + file + "]"); + new ResourcesCopier().copy(outputFolders, file); + } + + List mavenPropertiesToDocsProjects(File file) { + Model model = PomReader.readPom(file); + Properties properties = model.getProperties(); + return properties.entrySet().stream() + .filter(e -> e.getKey().toString().endsWith(".version")) + .map(e -> new Project(e.getKey().toString().replace(".version", ""), + e.getValue().toString())) + .collect(Collectors.toCollection(LinkedList::new)); + } + + Project springBootVersion(File file) { + Model model = PomReader.readPom(file); + return new Project("spring-boot", model.getParent().getVersion()); + } + + List downloadSources(File outputFolder, List projects, + String repoUrl) { + ArtifactFetcher fetcher = new ArtifactFetcher(outputFolder, repoUrl); + try { + List> futures = new LinkedList<>(); + for (Project project : projects) { + futures.add(service.submit(() -> fetcher.unpackedDocs(project))); + } + List files = futures.stream().map(future -> { + try { + return future.get(5, TimeUnit.MINUTES); + } + catch (Exception e) { + throw new IllegalStateException(e); + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + info("Unpacked docs modules to the following directories [" + files + "]"); + return files; + } + finally { + service.shutdown(); + } + } + + List mergeConfigurationProperties(File outputFolderWithDocs) { + ConfigurationPropertiesAggregator aggregator = new ConfigurationPropertiesAggregator(); + return aggregator.mergedConfigurationProperties(outputFolderWithDocs.toPath()); + } + + File renderAsciidocTemplates(File generatedTrainDocs, List projects, + List configurationProperties) { + TemplateGenerator templateGenerator = new TemplateGenerator(generatedTrainDocs); + return templateGenerator.generate(projects, configurationProperties); + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/HandlebarsHelper.java b/train-docs/src/main/java/org/springframework/cloud/internal/HandlebarsHelper.java new file mode 100644 index 0000000..7c8b9f8 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/HandlebarsHelper.java @@ -0,0 +1,48 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.IOException; + +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.helper.StringHelpers; +import com.github.jknack.handlebars.io.ClassPathTemplateLoader; + +/** + * @author Marcin Grzejszczak + */ +final class HandlebarsHelper { + + private HandlebarsHelper() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + public static Template template(String templateName) { + try { + Handlebars handlebars = new Handlebars( + new ClassPathTemplateLoader("/templates/spring-cloud/")); + handlebars.registerHelper("replace", StringHelpers.replace); + handlebars.registerHelper("capitalizeFirst", StringHelpers.capitalizeFirst); + return handlebars.compile(templateName); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/Logger.java b/train-docs/src/main/java/org/springframework/cloud/internal/Logger.java new file mode 100644 index 0000000..41cde9b --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/Logger.java @@ -0,0 +1,33 @@ +/* + * 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 org.springframework.cloud.internal; + +final class Logger { + + private Logger() { + + } + + static void info(String text) { + System.out.println(text); + } + + static void error(String text) { + System.err.println(text); + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/PomReader.java b/train-docs/src/main/java/org/springframework/cloud/internal/PomReader.java new file mode 100644 index 0000000..a453d89 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/PomReader.java @@ -0,0 +1,68 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Class that reads poms as {@link Model}. + * + * @author Marcin Grzejszczak + */ +final class PomReader { + + private PomReader() { + throw new IllegalStateException("Shouldn't instantiate a utility class"); + } + + /** + * Returns a parsed POM. + * @param pom location to the pom + * @return parsed model + */ + public static Model readPom(File pom) { + if (!pom.exists()) { + throw new IllegalStateException( + "File [" + pom.getAbsolutePath() + "] not found"); + } + String fileText = ""; + try (Reader reader = new FileReader(pom)) { + if (pom.isFile()) { + fileText = new String(Files.readAllBytes(pom.toPath())); + } + MavenXpp3Reader xpp3Reader = new MavenXpp3Reader(); + return xpp3Reader.read(reader); + } + catch (XmlPullParserException | IOException e) { + if (pom.isFile() && fileText.length() == 0) { + throw new IllegalStateException( + "File [" + pom.getAbsolutePath() + "] is empty", e); + } + throw new IllegalStateException( + "Failed to read file: " + pom.getAbsolutePath(), e); + } + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/Project.java b/train-docs/src/main/java/org/springframework/cloud/internal/Project.java new file mode 100644 index 0000000..77a3ec1 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/Project.java @@ -0,0 +1,64 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.util.Objects; + +class Project { + + final String name; + + final String version; + + Project(String name, String version) { + this.name = name; + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Project project = (Project) o; + return Objects.equals(name, project.name) + && Objects.equals(version, project.version); + } + + @Override + public int hashCode() { + return Objects.hash(name, version); + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return "Project{" + "name='" + this.name + '\'' + ", version='" + this.version + + '\'' + '}'; + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/ResourcesCopier.java b/train-docs/src/main/java/org/springframework/cloud/internal/ResourcesCopier.java new file mode 100644 index 0000000..e45681b --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/ResourcesCopier.java @@ -0,0 +1,44 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.springframework.util.FileSystemUtils; + +final class ResourcesCopier { + + void copy(List baseDirs, File output) { + File images = new File(output, "images"); + baseDirs.forEach(input -> { + try { + File inputImages = new File(input, "images"); + if (!inputImages.exists()) { + return; + } + Logger.info("Will copy [" + inputImages + "] files to [" + images + "]"); + FileSystemUtils.copyRecursively(inputImages, images); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/TemplateGenerator.java b/train-docs/src/main/java/org/springframework/cloud/internal/TemplateGenerator.java new file mode 100644 index 0000000..229baf4 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/TemplateGenerator.java @@ -0,0 +1,103 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.github.jknack.handlebars.Template; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import static org.springframework.cloud.internal.Logger.info; + +/** + * @author Marcin Grzejszczak + */ +class TemplateGenerator { + + final File outputFolder; + + TemplateGenerator(File outputFolder) { + this.outputFolder = outputFolder; + this.outputFolder.mkdirs(); + } + + File generate(List projects, + List configurationProperties) { + PathMatchingResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver(); + try { + Resource[] resources = resourceLoader + .getResources("templates/spring-cloud/*.hbs"); + List templateProjects = templateProjects(projects); + for (Resource resource : resources) { + File templateFile = resource.getFile(); + File outputFile = new File(outputFolder, renameTemplate(templateFile)); + Template template = template(templateFile.getName().replace(".hbs", "")); + Map map = new HashMap<>(); + map.put("projects", projects); + map.put("springCloudProjects", templateProjects); + map.put("properties", configurationProperties); + String applied = template.apply(map); + Files.write(outputFile.toPath(), applied.getBytes()); + info("Successfully rendered [" + outputFile.getAbsolutePath() + "]"); + } + } + catch (IOException e) { + throw new IllegalStateException(e); + } + return outputFolder; + } + + private List templateProjects(List projects) { + return projects.stream() + .filter(project -> project.name.startsWith("spring-cloud-")) + .map(project -> { + if (project.name.contains("spring-cloud-task")) { + return new TemplateProject(project.name, project.version, + "{basedir}/" + project.name + + "/spring-cloud-task-docs/src/main/asciidoc/index.adoc[leveloffset=+1]"); + } + return new TemplateProject(project.name, project.version, + "{basedir}/" + project.name + "/docs/src/main/asciidoc/" + + project.name + ".adoc[leveloffset=+1]"); + }).collect(Collectors.toCollection(LinkedList::new)); + } + + private String renameTemplate(File templateFile) { + String templateName = templateFile.getName(); + if (templateName.endsWith("-pdf.hbs")) { + return templateName.replace("-pdf.hbs", ".pdfadoc"); + } + else if (templateName.endsWith("-single.hbs")) { + return templateName.replace("-single.hbs", ".htmlsingleadoc"); + } + return templateName.replace(".hbs", ".adoc"); + } + + private Template template(String template) { + return HandlebarsHelper.template(template); + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/TemplateProject.java b/train-docs/src/main/java/org/springframework/cloud/internal/TemplateProject.java new file mode 100644 index 0000000..26112d5 --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/TemplateProject.java @@ -0,0 +1,65 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.util.Objects; + +class TemplateProject { + + final String name; + + final String version; + + final String include; + + TemplateProject(String name, String version, String include) { + this.name = name; + this.version = version; + this.include = include; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TemplateProject that = (TemplateProject) o; + return Objects.equals(name, that.name) && Objects.equals(version, that.version) + && Objects.equals(include, that.include); + } + + @Override + public int hashCode() { + return Objects.hash(name, version, include); + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getInclude() { + return include; + } + +} diff --git a/train-docs/src/main/java/org/springframework/cloud/internal/ZipCategory.java b/train-docs/src/main/java/org/springframework/cloud/internal/ZipCategory.java new file mode 100644 index 0000000..4099cef --- /dev/null +++ b/train-docs/src/main/java/org/springframework/cloud/internal/ZipCategory.java @@ -0,0 +1,98 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.springframework.util.StreamUtils; + +/** + * Based on + * https://github.com/ + * timyates/groovy-common-extensions. + * + * Category for {@link File} that adds a method that allows you to unzip a given file to a + * specified location + * + * @author Marcin Grzejszczak + */ +final class ZipCategory { + + private ZipCategory() { + throw new IllegalStateException("Can't instantiate a utility class"); + } + + /** + * Unzips this file. If the destination directory is not provided, it will + * fall back to this file's parent directory. + * @param self file + * @param destination (optional), the destination directory where this file's content + * will be unzipped to. + * @return a {@link Collection} of unzipped {@link File} objects. + */ + static Collection unzipTo(File self, File destination) { + checkUnzipDestination(destination); + // if destination directory is not given, we'll fall back to the parent directory + // of 'self' + if (destination == null) { + destination = new File(self.getParent()); + } + List unzippedFiles = new ArrayList<>(); + try (InputStream fileInputStream = Files.newInputStream(self.toPath())) { + try (ZipInputStream zipInput = new ZipInputStream(fileInputStream)) { + for (ZipEntry entry = zipInput + .getNextEntry(); entry != null; entry = zipInput.getNextEntry()) { + if (!entry.isDirectory()) { + final File file = new File(destination, entry.getName()); + if (file.getParentFile() != null) { + file.getParentFile().mkdirs(); + } + try (OutputStream output = Files.newOutputStream(file.toPath())) { + StreamUtils.copy(zipInput, output); + } + unzippedFiles.add(file); + } + else { + final File dir = new File(destination, entry.getName()); + dir.mkdirs(); + unzippedFiles.add(dir); + } + } + } + } + catch (IOException e) { + throw new IllegalStateException("Cannot unzip archive", e); + } + return unzippedFiles; + } + + private static void checkUnzipDestination(File file) { + if (file != null && !file.isDirectory()) { + throw new IllegalArgumentException("'destination' has to be a directory."); + } + } + +} diff --git a/train-docs/src/main/resources/templates/spring-cloud/_attributes.hbs b/train-docs/src/main/resources/templates/spring-cloud/_attributes.hbs new file mode 100644 index 0000000..25ad2eb --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/_attributes.hbs @@ -0,0 +1,15 @@ +:doctype: book +:idprefix: +:idseparator: - +:toc: left +:toclevels: 4 +:tabsize: 4 +:numbered: +:sectanchors: +:sectnums: +:icons: font +:hide-uri-scheme: +:docinfo: shared,private + +:project-full-name: Spring Cloud +:project-name: spring-cloud \ No newline at end of file diff --git a/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-attributes.hbs b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-attributes.hbs new file mode 100644 index 0000000..787cf15 --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-attributes.hbs @@ -0,0 +1,2 @@ +{{#each projects}}:{{name}}-version: {{version}} +{{/each}} diff --git a/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-links.hbs b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-links.hbs new file mode 100644 index 0000000..4be4b68 --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-links.hbs @@ -0,0 +1,2 @@ +{{#each springCloudProjects}} https://docs.spring.io/{{name}}/docs/{{version}}/reference/html/[{{name}}] :: {{name}} Reference Documentation, version {{version}} +{{/each}} diff --git a/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-versions.hbs b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-versions.hbs new file mode 100644 index 0000000..6ae15ac --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/_spring-cloud-versions.hbs @@ -0,0 +1,2 @@ +{{#each projects}} |{{name}}|{{version}} +{{/each}} \ No newline at end of file diff --git a/train-docs/src/main/resources/templates/spring-cloud/configprops.hbs b/train-docs/src/main/resources/templates/spring-cloud/configprops.hbs new file mode 100644 index 0000000..267b519 --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/configprops.hbs @@ -0,0 +1,7 @@ +|=== +|Name | Default | Description + +{{#each properties}} |{{name}} | {{defaultValue}} | {{description}} +{{/each}} + +|=== \ No newline at end of file diff --git a/train-docs/src/main/resources/templates/spring-cloud/documentation-overview.hbs b/train-docs/src/main/resources/templates/spring-cloud/documentation-overview.hbs new file mode 100644 index 0000000..76543bf --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/documentation-overview.hbs @@ -0,0 +1,38 @@ +[[documentation]] += Spring Cloud Documentation +include::_attributes.adoc[] + +:docs-url: https://docs.spring.io/spring-cloud/docs/{spring-cloud-version} + +This section provides a brief overview of Spring Cloud reference documentation. It serves +as a map for the rest of the document. + + + +[[documentation-about]] +== About the Documentation + +The {project-full-name} reference guide is available as + +* {docs-url}/reference/html[Multi-page HTML] +* {docs-url}/reference/htmlsingle[Single-page HTML] +* {docs-url}/reference/pdf/{project-name}.pdf[PDF] + +Copies of this document may be made for your own use and for distribution to others, +provided that you do not charge any fee for such copies and further provided that each +copy contains this Copyright Notice, whether distributed in print or electronically. + + + +[[documentation-getting-help]] +== Getting Help +If you have trouble with {project-full-name}, we would like to help. + +* Learn the {project-full-name} basics. If you are +starting out with {project-full-name}, try one of the https://spring.io/guides[guides]. +* Ask a question. We monitor https://stackoverflow.com[stackoverflow.com] for questions +tagged with https://stackoverflow.com/tags/spring-cloud[`spring-cloud`]. +* Chat with us at https://gitter.im/spring-cloud/spring-cloud[{project-full-name} Gitter] + +NOTE: All of {project-full-name} is open source, including the documentation. If you find +problems with the docs or if you want to improve them, please get involved. \ No newline at end of file diff --git a/train-docs/src/main/resources/templates/spring-cloud/legal.hbs b/train-docs/src/main/resources/templates/spring-cloud/legal.hbs new file mode 100644 index 0000000..048c5ad --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/legal.hbs @@ -0,0 +1,11 @@ +[legal] += Legal + +{spring-cloud-version} + +Copyright © 2012-2020 + +Copies of this document may be made for your own use and for distribution to +others, provided that you do not charge any fee for such copies and further +provided that each copy contains this Copyright Notice, whether distributed in +print or electronically. \ No newline at end of file diff --git a/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-pdf.hbs b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-pdf.hbs new file mode 100644 index 0000000..1844935 --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-pdf.hbs @@ -0,0 +1,57 @@ += Spring Cloud + +include::_attributes.adoc[] +include::_spring-cloud-attributes.adoc[] + +:basedir: {project-root} +:project-full-name: Spring Cloud +:project-name: spring-cloud + +Spring Cloud provides tools for developers to quickly build some of +the common patterns in distributed systems (e.g. configuration +management, service discovery, circuit breakers, intelligent routing, +micro-proxy, control bus). Coordination of +distributed systems leads to boiler plate patterns, and using Spring +Cloud developers can quickly stand up services and applications that +implement those patterns. They will work well in any distributed +environment, including the developer's own laptop, bare metal data +centres, and managed platforms such as Cloud Foundry. + +Release Train Version: *{spring-cloud-version}* + +Supported Boot Version: *{spring-boot-version}* + +== Features + +Spring Cloud focuses on providing good out of box experience for typical use cases +and extensibility mechanism to cover others. + +* Distributed/versioned configuration +* Service registration and discovery +* Routing +* Service-to-service calls +* Load balancing +* Circuit Breakers +* Distributed messaging + +[[cloud-documentation-versions]] +== Release Train Versions + +.Release Train Project Versions +|=== +|Project Name| Project Version + +include::_spring-cloud-versions.adoc[] +|=== + +{{=<% %>=}} +<%#each springCloudProjects%> +:project-root: {basedir}/<%name%> +:project-version: {<%name%>-version} +include::<%include%> +<%/each%> +<%={{ }}=%> + += Appendix: Compendium of Configuration Properties + +include::configprops.adoc[leveloffset=+1] diff --git a/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-single.hbs b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-single.hbs new file mode 100644 index 0000000..1844935 --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud-single.hbs @@ -0,0 +1,57 @@ += Spring Cloud + +include::_attributes.adoc[] +include::_spring-cloud-attributes.adoc[] + +:basedir: {project-root} +:project-full-name: Spring Cloud +:project-name: spring-cloud + +Spring Cloud provides tools for developers to quickly build some of +the common patterns in distributed systems (e.g. configuration +management, service discovery, circuit breakers, intelligent routing, +micro-proxy, control bus). Coordination of +distributed systems leads to boiler plate patterns, and using Spring +Cloud developers can quickly stand up services and applications that +implement those patterns. They will work well in any distributed +environment, including the developer's own laptop, bare metal data +centres, and managed platforms such as Cloud Foundry. + +Release Train Version: *{spring-cloud-version}* + +Supported Boot Version: *{spring-boot-version}* + +== Features + +Spring Cloud focuses on providing good out of box experience for typical use cases +and extensibility mechanism to cover others. + +* Distributed/versioned configuration +* Service registration and discovery +* Routing +* Service-to-service calls +* Load balancing +* Circuit Breakers +* Distributed messaging + +[[cloud-documentation-versions]] +== Release Train Versions + +.Release Train Project Versions +|=== +|Project Name| Project Version + +include::_spring-cloud-versions.adoc[] +|=== + +{{=<% %>=}} +<%#each springCloudProjects%> +:project-root: {basedir}/<%name%> +:project-version: {<%name%>-version} +include::<%include%> +<%/each%> +<%={{ }}=%> + += Appendix: Compendium of Configuration Properties + +include::configprops.adoc[leveloffset=+1] diff --git a/train-docs/src/main/resources/templates/spring-cloud/spring-cloud.hbs b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud.hbs new file mode 100644 index 0000000..531a3ed --- /dev/null +++ b/train-docs/src/main/resources/templates/spring-cloud/spring-cloud.hbs @@ -0,0 +1,44 @@ += Spring Cloud + +include::_attributes.adoc[] +include::_spring-cloud-attributes.adoc[] + +:basedir: {project-root} + +Spring Cloud provides tools for developers to quickly build some of +the common patterns in distributed systems (e.g. configuration +management, service discovery, circuit breakers, intelligent routing, +micro-proxy, control bus). Coordination of +distributed systems leads to boiler plate patterns, and using Spring +Cloud developers can quickly stand up services and applications that +implement those patterns. They will work well in any distributed +environment, including the developer's own laptop, bare metal data +centres, and managed platforms such as Cloud Foundry. + +Release Train Version: *{spring-cloud-version}* + +Supported Boot Version: *{spring-boot-version}* + +== Features + +Spring Cloud focuses on providing good out of box experience for typical use cases +and extensibility mechanism to cover others. + +* Distributed/versioned configuration +* Service registration and discovery +* Routing +* Service-to-service calls +* Load balancing +* Circuit Breakers +* Distributed messaging + +The reference documentation consists of the following sections: + +[horizontal] +<> :: Legal information. +<> :: About the Documentation, Getting Help, First Steps, and more. +include::_spring-cloud-links.adoc[] + += Appendix: Compendium of Configuration Properties + +link:configprops.html[Spring Cloud configuration properties] diff --git a/train-docs/src/test/java/org/springframework/cloud/internal/GenerateReleaseTrainDocsTests.java b/train-docs/src/test/java/org/springframework/cloud/internal/GenerateReleaseTrainDocsTests.java new file mode 100644 index 0000000..1d69904 --- /dev/null +++ b/train-docs/src/test/java/org/springframework/cloud/internal/GenerateReleaseTrainDocsTests.java @@ -0,0 +1,111 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; + +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Test; + +import org.springframework.util.FileSystemUtils; + +public class GenerateReleaseTrainDocsTests { + + @Test + void should_return_a_list_of_docs_modules_to_download() throws URISyntaxException { + File testPom = new File( + GenerateReleaseTrainDocsTests.class.getResource("/test/pom.xml").toURI()); + + List projects = new GenerateReleaseTrainDocs() + .mavenPropertiesToDocsProjects(testPom); + + BDDAssertions.then(projects).extracting("name").containsOnly("spring-cloud-bus", + "spring-cloud-build", "spring-cloud-cloudfoundry", "spring-cloud-commons", + "spring-cloud-circuitbreaker", "spring-cloud-config", + "spring-cloud-consul", "spring-cloud-contract", "spring-cloud-function", + "spring-cloud-gateway", "spring-cloud-kubernetes", "spring-cloud-netflix", + "spring-cloud-openfeign", "spring-cloud-security", "spring-cloud-sleuth", + "spring-cloud-stream", "spring-cloud-task", "spring-cloud-vault", + "spring-cloud-zookeeper", "spring-cloud-cli"); + BDDAssertions.then(projects) + .contains(new Project("spring-cloud-bus", "1.2.3-SNAPSHOT")); + } + + @Test + void should_unpack_starters_docs() throws URISyntaxException { + File testPom = new File(GenerateReleaseTrainDocsTests.class + .getResource("/test/sleuth-only.xml").toURI()); + File unzippedDocs = new File("target/test-unpacked-docs/"); + List projects = new GenerateReleaseTrainDocs() + .mavenPropertiesToDocsProjects(testPom); + + new GenerateReleaseTrainDocs().downloadSources(unzippedDocs, projects, + "https://github.com/spring-cloud/"); + + BDDAssertions.then(unzippedDocs).isNotEmptyDirectory(); + } + + @Test + void should_generate_adocs_from_templates() { + File file = new File("target/test-train-docs"); + FileSystemUtils.deleteRecursively(file); + List projects = Arrays.asList(new Project("spring-cloud-foo", "1.0.0"), + new Project("spring-cloud-bar", "2.0.0"), + new Project("spring-boot", "3.0.0"), + new Project("spring-cloud", "4.0.0")); + List configurationProperties = Arrays.asList( + new ConfigurationProperty("first", "firstDefault", "firstDescription"), + new ConfigurationProperty("second", "secondDefault", + "secondDescription")); + + File outputFolder = new TemplateGenerator(file).generate(projects, + configurationProperties); + + BDDAssertions.then(outputFolder).isNotEmptyDirectory(); + } + + @Test + void should_generate_adocs_from_spring_cloud_sleuth_docs() + throws URISyntaxException, IOException { + File generatedAdocs = new File("target/test-train-sleuth-docs"); + FileSystemUtils.deleteRecursively(generatedAdocs); + File testPom = new File(GenerateReleaseTrainDocsTests.class + .getResource("/test/sleuth-only.xml").toURI()); + File starterPom = new File(GenerateReleaseTrainDocsTests.class + .getResource("/test/starter-pom.xml").toURI()); + File unzippedDocs = new File("target/test-unpacked-sleuth-docs/"); + + GenerateReleaseTrainDocs.main(testPom.getAbsolutePath(), + starterPom.getAbsolutePath(), "https://github.com/spring-cloud/", + unzippedDocs.getAbsolutePath(), generatedAdocs.getAbsolutePath()); + + BDDAssertions.then(generatedAdocs).isNotEmptyDirectory(); + BDDAssertions.then(configProps(generatedAdocs)).contains( + "|spring.sleuth.async.configurer.enabled | true | Enable default AsyncConfigurer."); + } + + private String configProps(File file) throws IOException { + return new String( + Files.readAllBytes(new File(file, "configprops.adoc").toPath())); + } + +} diff --git a/train-docs/src/test/java/org/springframework/cloud/internal/TestThatGeneratesTheFinalReleaseTrainDocumentationTests.java b/train-docs/src/test/java/org/springframework/cloud/internal/TestThatGeneratesTheFinalReleaseTrainDocumentationTests.java new file mode 100644 index 0000000..9ba36a4 --- /dev/null +++ b/train-docs/src/test/java/org/springframework/cloud/internal/TestThatGeneratesTheFinalReleaseTrainDocumentationTests.java @@ -0,0 +1,52 @@ +/* + * 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 org.springframework.cloud.internal; + +import java.io.File; + +import org.assertj.core.api.BDDAssertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import org.springframework.util.StringUtils; + +public class TestThatGeneratesTheFinalReleaseTrainDocumentationTests { + + /** + * FOR SOME REASON I CAN'T RUN MAIN CLASS FROM MAVEN AS A WORKAOUND WE WILL RUN THIS + * TEST TO GENERATE THE DOCS + */ + @Test + @Disabled + void should_generate_adocs_with_values_from_system_property() { + // System property needs to be passed + Assumptions.assumeTrue(StringUtils.hasText(System.getProperty("bomPath"))); + + String bomPath = System.getProperty("bomPath"); + String starterParentPath = System.getProperty("starterParentPath"); + String repoUrl = System.getProperty("repoUrl"); + String unzippedDocs = System.getProperty("unzippedDocs"); + String generatedTrainDocs = System.getProperty("generatedTrainDocs"); + + GenerateReleaseTrainDocs.main(bomPath, starterParentPath, repoUrl, unzippedDocs, + generatedTrainDocs); + + BDDAssertions.then(new File(generatedTrainDocs)).isNotEmptyDirectory(); + } + +} diff --git a/train-docs/src/test/resources/logback.xml b/train-docs/src/test/resources/logback.xml new file mode 100644 index 0000000..96d75b7 --- /dev/null +++ b/train-docs/src/test/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/train-docs/src/test/resources/test/pom.xml b/train-docs/src/test/resources/test/pom.xml new file mode 100644 index 0000000..cc367cd --- /dev/null +++ b/train-docs/src/test/resources/test/pom.xml @@ -0,0 +1,255 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-dependencies-parent + 3.0.0-SNAPSHOT + + + spring-cloud-dependencies + 2020.0.0-SNAPSHOT + spring-cloud-dependencies + Spring Cloud Dependencies + pom + + ${basedir}/../.. + 1.2.3-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 2.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.1.0-SNAPSHOT + 3.0.0-SNAPSHOT + 2.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.1.0-SNAPSHOT + 2.3.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + + + + + + org.springframework.cloud + spring-cloud-commons-dependencies + ${spring-cloud-commons.version} + pom + import + + + org.springframework.cloud + spring-cloud-netflix-dependencies + ${spring-cloud-netflix.version} + pom + import + + + org.springframework.cloud + spring-cloud-stream-dependencies + ${spring-cloud-stream.version} + pom + import + + + org.springframework.cloud + spring-cloud-task-dependencies + ${spring-cloud-task.version} + pom + import + + + org.springframework.cloud + spring-cloud-circuitbreaker-dependencies + ${spring-cloud-circuitbreaker.version} + pom + import + + + org.springframework.cloud + spring-cloud-config-dependencies + ${spring-cloud-config.version} + pom + import + + + org.springframework.cloud + spring-cloud-function-dependencies + ${spring-cloud-function.version} + pom + import + + + org.springframework.cloud + spring-cloud-gateway-dependencies + ${spring-cloud-gateway.version} + pom + import + + + org.springframework.cloud + spring-cloud-consul-dependencies + ${spring-cloud-consul.version} + pom + import + + + org.springframework.cloud + spring-cloud-sleuth-dependencies + ${spring-cloud-sleuth.version} + pom + import + + + org.springframework.cloud + spring-cloud-vault-dependencies + ${spring-cloud-vault.version} + pom + import + + + org.springframework.cloud + spring-cloud-zookeeper-dependencies + ${spring-cloud-zookeeper.version} + pom + import + + + org.springframework.cloud + spring-cloud-security-dependencies + ${spring-cloud-security.version} + pom + import + + + org.springframework.cloud + spring-cloud-cloudfoundry-dependencies + ${spring-cloud-cloudfoundry.version} + pom + import + + + org.springframework.cloud + spring-cloud-bus-dependencies + ${spring-cloud-bus.version} + pom + import + + + org.springframework.cloud + spring-cloud-contract-dependencies + ${spring-cloud-contract.version} + pom + import + + + org.springframework.cloud + spring-cloud-openfeign-dependencies + ${spring-cloud-openfeign.version} + pom + import + + + org.springframework.cloud + spring-cloud-kubernetes-dependencies + ${spring-cloud-kubernetes.version} + pom + import + + + + + + + org.codehaus.mojo + flatten-maven-plugin + false + + + + flatten + process-resources + + flatten + + + true + bom + + expand + keep + keep + remove + + + + + flatten-clean + clean + + clean + + + + + + + + + spring + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + + + diff --git a/train-docs/src/test/resources/test/sleuth-only.xml b/train-docs/src/test/resources/test/sleuth-only.xml new file mode 100644 index 0000000..20eac36 --- /dev/null +++ b/train-docs/src/test/resources/test/sleuth-only.xml @@ -0,0 +1,236 @@ + + + 4.0.0 + + org.springframework.cloud + spring-cloud-dependencies-parent + 3.0.0-SNAPSHOT + + + spring-cloud-dependencies + 2020.0.0-SNAPSHOT + spring-cloud-dependencies + Spring Cloud Dependencies + pom + + ${basedir}/../.. + 3.0.0-SNAPSHOT + + + + + + org.springframework.cloud + spring-cloud-commons-dependencies + ${spring-cloud-commons.version} + pom + import + + + org.springframework.cloud + spring-cloud-netflix-dependencies + ${spring-cloud-netflix.version} + pom + import + + + org.springframework.cloud + spring-cloud-stream-dependencies + ${spring-cloud-stream.version} + pom + import + + + org.springframework.cloud + spring-cloud-task-dependencies + ${spring-cloud-task.version} + pom + import + + + org.springframework.cloud + spring-cloud-circuitbreaker-dependencies + ${spring-cloud-circuitbreaker.version} + pom + import + + + org.springframework.cloud + spring-cloud-config-dependencies + ${spring-cloud-config.version} + pom + import + + + org.springframework.cloud + spring-cloud-function-dependencies + ${spring-cloud-function.version} + pom + import + + + org.springframework.cloud + spring-cloud-gateway-dependencies + ${spring-cloud-gateway.version} + pom + import + + + org.springframework.cloud + spring-cloud-consul-dependencies + ${spring-cloud-consul.version} + pom + import + + + org.springframework.cloud + spring-cloud-sleuth-dependencies + ${spring-cloud-sleuth.version} + pom + import + + + org.springframework.cloud + spring-cloud-vault-dependencies + ${spring-cloud-vault.version} + pom + import + + + org.springframework.cloud + spring-cloud-zookeeper-dependencies + ${spring-cloud-zookeeper.version} + pom + import + + + org.springframework.cloud + spring-cloud-security-dependencies + ${spring-cloud-security.version} + pom + import + + + org.springframework.cloud + spring-cloud-cloudfoundry-dependencies + ${spring-cloud-cloudfoundry.version} + pom + import + + + org.springframework.cloud + spring-cloud-bus-dependencies + ${spring-cloud-bus.version} + pom + import + + + org.springframework.cloud + spring-cloud-contract-dependencies + ${spring-cloud-contract.version} + pom + import + + + org.springframework.cloud + spring-cloud-openfeign-dependencies + ${spring-cloud-openfeign.version} + pom + import + + + org.springframework.cloud + spring-cloud-kubernetes-dependencies + ${spring-cloud-kubernetes.version} + pom + import + + + + + + + org.codehaus.mojo + flatten-maven-plugin + false + + + + flatten + process-resources + + flatten + + + true + bom + + expand + keep + keep + remove + + + + + flatten-clean + clean + + clean + + + + + + + + + spring + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + + + diff --git a/train-docs/src/test/resources/test/starter-pom.xml b/train-docs/src/test/resources/test/starter-pom.xml new file mode 100644 index 0000000..7e14308 --- /dev/null +++ b/train-docs/src/test/resources/test/starter-pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.0-SNAPSHOT + + org.springframework.cloud + spring-cloud-starter-parent + 2020.0.0-SNAPSHOT + spring-cloud-starter-parent + Spring Cloud Starter Parent + pom + https://projects.spring.io/spring-cloud + + Pivotal Software, Inc. + https://www.spring.io + + + ${basedir}/../.. + 2020.0.0-SNAPSHOT + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + https://github.com/spring-cloud + + spring-docs + + scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-cloud/docs/${project.artifactId}/${project.version} + + + + repo.spring.io + Spring Release Repository + https://repo.spring.io/libs-release-local + + + repo.spring.io + Spring Snapshot Repository + https://repo.spring.io/libs-snapshot-local + + + + + spring + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/libs-milestone-local + + false + + + + + + milestone + + + repo.spring.io + Spring Milestone Repository + https://repo.spring.io/libs-milestone-local + + + + + bintray + + + bintray + Jcenter Repository + https://api.bintray.com/maven/spring/jars/org.springframework.cloud:${bintray.package} + + + + + central + + + + org.apache.maven.plugins + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + +