This commit is contained in:
Marcin Grzejszczak
2020-06-08 18:42:04 +02:00
parent 4152fa576b
commit 7e67958f46
23 changed files with 724 additions and 232 deletions

View File

@@ -18,8 +18,9 @@
<maven.version>3.6.3</maven.version>
<!-- Resolver has to be aligned with Maven (e.g resolver 1.4 and Maven 3.6 or resolver 1.3 and Maven 3.5) -->
<maven.resolver.version>1.4.2</maven.resolver.version>
<maven.resolver.version>1.4.1</maven.resolver.version>
<handlebars.version>4.0.6</handlebars.version>
<asciidoctorj.version>2.1.0</asciidoctorj.version>
</properties>
<dependencies>
<dependency>
@@ -40,6 +41,27 @@
<groupId>org.apache.maven</groupId>
<artifactId>maven-settings-builder</artifactId>
<version>${maven.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>${maven.version}</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.sonatype.plexus</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
@@ -51,6 +73,11 @@
<artifactId>maven-resolver-impl</artifactId>
<version>${maven.resolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-util</artifactId>
<version>${maven.resolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-file</artifactId>
@@ -89,6 +116,18 @@
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemProperties>
<bomPath>${maven.multiModuleProjectDirectory}/spring-cloud-dependencies/pom.xml</bomPath>
<repoUrl>${maven-deploy-plugin.deployZipUrl}</repoUrl>
<unzippedDocs>${project.build.directory}/unpacked-docs/</unzippedDocs>
<generatedTrainDocs>${project.build.directory}/train-docs/</generatedTrainDocs>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
@@ -102,10 +141,38 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<!-- Can't make this work, will generate the docs from a test as a workaround -->
<!--
<executions>
<execution>
<id>run-template-generation</id>
<phase>prepare-package</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<includePluginDependencies>true</includePluginDependencies>
<mainClass>org.springframework.cloud.internal.GenerateReleaseTrainDocs</mainClass>
<arguments>
<argument>${maven.multiModuleProjectDirectory}/spring-cloud-dependencies/pom.xml</argument>
<argument>${maven-deploy-plugin.deployZipUrl}</argument>
<argument>${project.build.directory}/unpacked-docs/</argument>
<argument>${project.build.directory}/train-docs/</argument>
</arguments>
</configuration>
</execution>
</executions>
-->
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<configuration>
<sourceDirectory>${project.build.directory}/train-docs/</sourceDirectory>
<attributes>
<project-root>${project.build.directory}/unpacked-docs/</project-root>
</attributes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>

View File

@@ -0,0 +1,155 @@
/*
* 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.net.URI;
import java.util.Collections;
import java.util.List;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuildingResult;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.internal.Logger.info;
class ArtifactFetcher {
private final File outputFolder;
private final RepositorySystem repositorySystem;
private final RepositorySystemSession session;
private final List<RemoteRepository> remoteRepository;
ArtifactFetcher(File outputFolder, String url) {
this.outputFolder = outputFolder;
this.repositorySystem = newRepositorySystem();
this.session = newSession(this.repositorySystem);
this.remoteRepository = Collections.singletonList(
new RemoteRepository.Builder("remote", "default", url).build());
}
File unpackedDocs(Project project) {
String artifactName = project.name;
String version = project.version;
try {
Artifact artifact = new DefaultArtifact("org.springframework.cloud",
artifactName, "sources", "jar", version);
ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepository,
null);
info(artifactName + ": Resolving artifact [" + artifact
+ "] using remote repositories " + this.remoteRepository);
ArtifactResult result = this.repositorySystem.resolveArtifact(this.session,
request);
info(artifactName + ": Resolved artifact [" + artifact + "] to "
+ result.getArtifact().getFile());
File unpackedDoc = unpackDoc(artifactName,
result.getArtifact().getFile().toURI());
info(artifactName + ": Unpacked file to [" + unpackedDoc.getAbsolutePath()
+ "]");
return unpackedDoc;
}
catch (IllegalStateException ise) {
throw ise;
}
catch (Exception e) {
// throw new IllegalStateException(
System.err.println(
"Exception occurred while trying to download an artifact with name ["
+ artifactName + "] and version [" + version
+ "] in remote repo [" + this.remoteRepository);
// ,e);
return null;
}
}
private File unpackDoc(String artifactName, URI stubJarUri) {
File unzippedDocs = new File(outputFolder, artifactName);
unzippedDocs.mkdirs();
info(artifactName + ": Unpacking stub from JAR [URI: " + stubJarUri + "]");
ZipCategory.unzipTo(new File(stubJarUri), unzippedDocs);
return unzippedDocs;
}
private RepositorySystem newRepositorySystem() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class,
BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
return locator.getService(RepositorySystem.class);
}
private RepositorySystemSession newSession(RepositorySystem system) {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN);
String localRepository = settings().getLocalRepository();
localRepository = StringUtils.hasText(localRepository) ? localRepository
: System.getProperty("user.home") + File.separator + ".m2"
+ File.separator + "repository";
LocalRepository localRepo = new LocalRepository(localRepository);
session.setLocalRepositoryManager(
system.newLocalRepositoryManager(session, localRepo));
return session;
}
private static Settings settings() {
SettingsBuilder builder = new DefaultSettingsBuilderFactory().newInstance();
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setUserSettingsFile(userSettings());
SettingsBuildingResult result;
try {
result = builder.build(request);
}
catch (SettingsBuildingException ex) {
throw new IllegalStateException(ex);
}
return result.getEffectiveSettings();
}
private static File userSettings() {
return new File(new File(System.getProperty("user.home")).getAbsoluteFile(),
File.separator + ".m2" + File.separator + "settings.xml");
}
}

View File

@@ -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<String> wordsToIgnore = Arrays.asList("|===",
"|Name | Default | Description");
List<ConfigurationProperty> 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.
@@ -17,149 +17,93 @@
package org.springframework.cloud.internal;
import java.io.File;
import java.net.URI;
import java.util.Collections;
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 org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuildingResult;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.internal.Logger.info;
public class GenerateReleaseTrainDocs {
private static final Logger log = LoggerFactory.getLogger(GenerateReleaseTrainDocs.class);
final ExecutorService service;
List<Project> toDocsList(File file) {
public static void main(String... args) {
File bomPath = new File(args[0]);
String repoUrl = args[1];
File unzippedDocs = new File(args[2]);
File generatedTrainDocs = new File(args[3]);
new GenerateReleaseTrainDocs().generate(bomPath, repoUrl, unzippedDocs,
generatedTrainDocs);
}
GenerateReleaseTrainDocs() {
this.service = Executors.newFixedThreadPool(4);
}
void generate(File bomPath, String repoUrl, File unzippedDocs,
File generatedTrainDocs) {
List<Project> projects = mavenPropertiesToDocsProjects(bomPath);
info("Found the following projects [" + projects + "]");
List<File> outputFolders = downloadDocsModules(unzippedDocs, projects, repoUrl);
List<ConfigurationProperty> configurationProperties = mergeConfigurationProperties(
unzippedDocs);
File file = renderAsciidocTemplates(generatedTrainDocs, projects,
configurationProperties);
info("Rendered docs templates to [" + file + "]");
new ResourcesCopier().copy(outputFolders, generatedTrainDocs);
}
List<Project> 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", "-docs"), e.getValue().toString()))
return properties.entrySet().stream()
.filter(e -> e.getKey().toString().endsWith(".version"))
.map(e -> new Project(e.getKey().toString().replace(".version", "-docs"),
e.getValue().toString()))
.collect(Collectors.toCollection(LinkedList::new));
}
List<File> downloadDocsModules(List<Project> projects, String repoUrl) {
ArtifactFetcher fetcher = new ArtifactFetcher(repoUrl);
return projects.stream().map(fetcher::unpackedDocs).collect(Collectors.toList());
}
}
class ArtifactFetcher {
private static final Logger log = LoggerFactory.getLogger(ArtifactFetcher.class);
private final RepositorySystem repositorySystem;
private final RepositorySystemSession session;
private final List<RemoteRepository> remoteRepository;
ArtifactFetcher(String url) {
this.repositorySystem = newRepositorySystem();
this.session = newSession(this.repositorySystem);
this.remoteRepository = Collections
.singletonList(new RemoteRepository.Builder("remote", "default", url).build());
}
File unpackedDocs(Project project) {
String artifactName = project.name;
String version = project.version;
List<File> downloadDocsModules(File outputFolder, List<Project> projects,
String repoUrl) {
ArtifactFetcher fetcher = new ArtifactFetcher(outputFolder, repoUrl);
try {
Artifact artifact = new DefaultArtifact("org.springframework.cloud", artifactName, "sources", "jar", version);
ArtifactRequest request = new ArtifactRequest(artifact, this.remoteRepository, null);
if (log.isDebugEnabled()) {
log.debug("Resolving artifact [" + artifact + "] using remote repositories " + this.remoteRepository);
List<Future<File>> futures = new LinkedList<>();
for (Project project : projects) {
futures.add(service.submit(() -> fetcher.unpackedDocs(project)));
}
ArtifactResult result = this.repositorySystem.resolveArtifact(this.session, request);
log.info("Resolved artifact [" + artifact + "] to " + result.getArtifact().getFile());
File unpackedDoc = unpackDoc(artifactName, result.getArtifact().getFile().toURI());
log.info("Unpacked file to [" + unpackedDoc.getAbsolutePath() + "]");
return unpackedDoc;
List<File> 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;
}
catch (IllegalStateException ise) {
throw ise;
}
catch (Exception e) {
throw new IllegalStateException("Exception occurred while trying to download an artifact with name ["
+ artifactName + "] and version [" + version + "] in remote repo [" + this.remoteRepository, e);
finally {
service.shutdown();
}
}
private File unpackDoc(String artifactName, URI stubJarUri) {
File unzippedDocs = new File("target/unpacked-docs/" + artifactName);
unzippedDocs.mkdirs();
log.info("Unpacking stub from JAR [URI: " + stubJarUri + "]");
ZipCategory.unzipTo(new File(stubJarUri), unzippedDocs);
return unzippedDocs;
List<ConfigurationProperty> mergeConfigurationProperties(File outputFolderWithDocs) {
ConfigurationPropertiesAggregator aggregator = new ConfigurationPropertiesAggregator();
return aggregator.mergedConfigurationProperties(outputFolderWithDocs.toPath());
}
private RepositorySystem newRepositorySystem() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
return locator.getService(RepositorySystem.class);
}
private RepositorySystemSession newSession(RepositorySystem system) {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN);
String localRepository = settings().getLocalRepository();
localRepository = StringUtils.hasText(localRepository) ? localRepository
: System.getProperty("user.home") + File.separator + ".m2"
+ File.separator + "repository";
LocalRepository localRepo = new LocalRepository(localRepository);
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
return session;
}
private static Settings settings() {
SettingsBuilder builder = new DefaultSettingsBuilderFactory().newInstance();
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setUserSettingsFile(userSettings());
SettingsBuildingResult result;
try {
result = builder.build(request);
}
catch (SettingsBuildingException ex) {
throw new IllegalStateException(ex);
}
return result.getEffectiveSettings();
}
private static File userSettings() {
return new File(new File(System.getProperty("user.home")).getAbsoluteFile(),
File.separator + ".m2" + File.separator + "settings.xml");
File renderAsciidocTemplates(File generatedTrainDocs, List<Project> projects,
List<ConfigurationProperty> configurationProperties) {
TemplateGenerator templateGenerator = new TemplateGenerator(generatedTrainDocs);
return templateGenerator.generate(projects, configurationProperties);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.
@@ -17,7 +17,6 @@
package org.springframework.cloud.internal;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
@@ -45,7 +44,8 @@ final class PomReader {
*/
public static Model readPom(File pom) {
if (!pom.exists()) {
throw new IllegalStateException("File [" + pom.getAbsolutePath() + "] not found");
throw new IllegalStateException(
"File [" + pom.getAbsolutePath() + "] not found");
}
String fileText = "";
try (Reader reader = new FileReader(pom)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.
@@ -38,7 +38,8 @@ class Project {
return false;
}
Project project = (Project) o;
return Objects.equals(name, project.name) && Objects.equals(version, project.version);
return Objects.equals(name, project.name)
&& Objects.equals(version, project.version);
}
@Override
@@ -46,4 +47,18 @@ class Project {
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
+ '\'' + '}';
}
}

View File

@@ -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<File> 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);
}
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 the original author or authors.
* 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.
@@ -20,46 +20,72 @@ 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 {
static final File OUTPUT_FOLDER = new File("target/train-docs");
final File outputFolder;
TemplateGenerator() {
OUTPUT_FOLDER.mkdirs();
TemplateGenerator(File outputFolder) {
this.outputFolder = outputFolder;
this.outputFolder.mkdirs();
}
File generate(List<Project> projects) {
File generate(List<Project> projects,
List<ConfigurationProperty> configurationProperties) {
PathMatchingResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();
List<Project> projectsWithoutDocs = projects.stream()
.map(p -> new Project(p.name.replace("-docs", ""), p.version))
.collect(Collectors.toList());
try {
Resource[] resources = resourceLoader.getResources("templates/spring-cloud/*.hbs");
Resource[] resources = resourceLoader
.getResources("templates/spring-cloud/*.hbs");
for (Resource resource : resources) {
File templateFile = resource.getFile();
File outputFile = new File(OUTPUT_FOLDER, templateFile.getName().replace(".hbs", ".adoc"));
File outputFile = new File(outputFolder, renameTemplate(templateFile));
Template template = template(templateFile.getName().replace(".hbs", ""));
Map<String, Object> map = new HashMap<>();
map.put("projects", projects);
map.put("properties", new HashMap<>());
map.put("projectsWithoutDocs", projectsWithoutDocs);
map.put("springCloudProjects",
projectsWithoutDocs.stream().filter(
project -> project.name.startsWith("spring-cloud-"))
.collect(Collectors.toCollection(LinkedList::new)));
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 this.OUTPUT_FOLDER;
return outputFolder;
}
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);

View File

@@ -1,2 +1,2 @@
{{#each projects}} :{{name}}-version: {{version}}
{{#each projectsWithoutDocs}}:{{name}}-version: {{version}}
{{/each}}

View File

@@ -1,2 +1,2 @@
{{#each projects}} https://docs.spring.io/{{name}}/docs/{{version}}/reference/html/[{{name}}] :: {{name}} Reference Documentation, version {{version}}
{{#each springCloudProjects}} https://docs.spring.io/{{name}}/docs/{{version}}/reference/html/[{{name}}] :: {{name}} Reference Documentation, version {{version}}
{{/each}}

View File

@@ -1,2 +1,2 @@
{{#each projects}} |{{name}}|{{version}}
{{#each projectsWithoutDocs}} |{{name}}|{{version}}
{{/each}}

View File

@@ -1,7 +1,7 @@
|===
|Name | Default | Description
{{#each properties}} |{{name}} | {{default}} | {{description}}
{{#each properties}} |{{name}} | {{defaultValue}} | {{description}}
{{/each}}
|===

View File

@@ -4,7 +4,6 @@ include::_attributes.adoc[]
include::_spring-cloud-attributes.adoc[]
:basedir: {project-root}
:stream_basedir: {basedir}
:project-full-name: Spring Cloud
:project-name: spring-cloud
@@ -45,11 +44,13 @@ and extensibility mechanism to cover others.
include::_spring-cloud-versions.adoc[]
|===
{{#each projects}}
:project-root: {basedir}/{{name}}-docs
:project-version: { {{name}}-version }
include::{basedir}/{{name}}-docs/{{name}}.adoc[leveloffset=+1]
{{/each}}
{{=<% %>=}}
<%#each springCloudProjects%>
:project-root: {unpackedDocsBaseDir}/<%name%>-docs
:project-version: {<%name%>-version}
include::{unpackedDocsBaseDir}/<%name%>-docs/<%name%>.adoc[leveloffset=+1]
<%/each%>
<%={{ }}=%>
= Appendix: Compendium of Configuration Properties

View File

@@ -4,7 +4,6 @@ include::_attributes.adoc[]
include::_spring-cloud-attributes.adoc[]
:basedir: {project-root}
:stream_basedir: {basedir}
:project-full-name: Spring Cloud
:project-name: spring-cloud
@@ -45,11 +44,13 @@ and extensibility mechanism to cover others.
include::_spring-cloud-versions.adoc[]
|===
{{#each projects}}
:project-root: {basedir}/{{name}}-docs
:project-version: { {{name}}-version }
include::{basedir}/{{name}}-docs/{{name}}.adoc[leveloffset=+1]
{{/each}}
{{=<% %>=}}
<%#each springCloudProjects%>
:project-root: {unpackedDocsBaseDir}/<%name%>-docs
:project-version: {<%name%>-version}
include::{unpackedDocsBaseDir}/<%name%>-docs/<%name%>.adoc[leveloffset=+1]
<%/each%>
<%={{ }}=%>
= Appendix: Compendium of Configuration Properties

View File

@@ -4,7 +4,6 @@ include::_attributes.adoc[]
include::_spring-cloud-attributes.adoc[]
:basedir: {project-root}
:stream_basedir: {basedir}
Spring Cloud provides tools for developers to quickly build some of
the common patterns in distributed systems (e.g. configuration
@@ -42,5 +41,4 @@ include::_spring-cloud-links.adoc[]
= Appendix: Compendium of Configuration Properties
// need to combine all configprops from all files into a single table
link:configprops.html[Spring Cloud configuration properties]

View File

@@ -0,0 +1,112 @@
/*
* 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<Project> projects = new GenerateReleaseTrainDocs()
.mavenPropertiesToDocsProjects(testPom);
BDDAssertions.then(projects).extracting("name").containsOnly(
"spring-cloud-bus-docs", "spring-cloud-build-docs",
"spring-cloud-cloudfoundry-docs", "spring-cloud-commons-docs",
"spring-cloud-circuitbreaker-docs", "spring-cloud-config-docs",
"spring-cloud-consul-docs", "spring-cloud-contract-docs",
"spring-cloud-function-docs", "spring-cloud-gateway-docs",
"spring-cloud-kubernetes-docs", "spring-cloud-netflix-docs",
"spring-cloud-openfeign-docs", "spring-cloud-security-docs",
"spring-cloud-sleuth-docs", "spring-cloud-stream-docs",
"spring-cloud-task-docs", "spring-cloud-vault-docs",
"spring-cloud-zookeeper-docs", "spring-cloud-cli-docs");
BDDAssertions.then(projects)
.contains(new Project("spring-cloud-bus-docs", "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/unpacked-docs/");
List<Project> projects = new GenerateReleaseTrainDocs()
.mavenPropertiesToDocsProjects(testPom);
new GenerateReleaseTrainDocs().downloadDocsModules(unzippedDocs, projects,
"https://repo.spring.io/libs-snapshot-local/");
BDDAssertions.then(unzippedDocs).isNotEmptyDirectory();
}
@Test
void should_generate_adocs_from_templates() {
File file = new File("target/train-docs");
FileSystemUtils.deleteRecursively(file);
List<Project> 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<ConfigurationProperty> 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/train-docs");
FileSystemUtils.deleteRecursively(generatedAdocs);
File testPom = new File(GenerateReleaseTrainDocsTests.class
.getResource("/test/sleuth-only.xml").toURI());
File unzippedDocs = new File("target/unpacked-docs/");
GenerateReleaseTrainDocs.main(testPom.getAbsolutePath(),
"https://repo.spring.io/libs-snapshot-local/",
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()));
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.internal;
import java.io.File;
import java.net.URISyntaxException;
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 SomethingTest {
@Test
void should_return_a_list_of_docs_modules_to_download() throws URISyntaxException {
File testPom = new File(SomethingTest.class.getResource("/test/pom.xml").toURI());
List<Project> projects = new GenerateReleaseTrainDocs().toDocsList(testPom);
BDDAssertions.then(projects)
.extracting("name")
.containsOnly("spring-cloud-bus-docs", "spring-cloud-build-docs",
"spring-cloud-cloudfoundry-docs", "spring-cloud-commons-docs", "spring-cloud-circuitbreaker-docs",
"spring-cloud-config-docs", "spring-cloud-consul-docs", "spring-cloud-contract-docs",
"spring-cloud-function-docs", "spring-cloud-gateway-docs", "spring-cloud-kubernetes-docs",
"spring-cloud-netflix-docs", "spring-cloud-openfeign-docs", "spring-cloud-security-docs",
"spring-cloud-sleuth-docs", "spring-cloud-stream-docs", "spring-cloud-task-docs",
"spring-cloud-vault-docs", "spring-cloud-zookeeper-docs", "spring-cloud-cli-docs");
BDDAssertions.then(projects).contains(new Project("spring-cloud-bus-docs", "1.2.3-SNAPSHOT"));
}
@Test
void should_unpack_starters_docs() throws URISyntaxException {
File testPom = new File(SomethingTest.class.getResource("/test/sleuth-only.xml").toURI());
String remoteRepo = "https://repo.spring.io/libs-snapshot-local/";
List<Project> projects = new GenerateReleaseTrainDocs().toDocsList(testPom);
List<File> files = new GenerateReleaseTrainDocs().downloadDocsModules(projects, "https://repo.spring.io/libs-snapshot-local/");
BDDAssertions.then(files)
.isNotEmpty();
}
@Test
void should_generate_the_single_pdf_multi_adoc_pages() {
}
@Test
void should_generate_adocs_from_templates() {
FileSystemUtils.deleteRecursively(TemplateGenerator.OUTPUT_FOLDER);
List<Project> projects = Arrays.asList(new Project("spring-cloud-foo", "1.0.0"), new Project("spring-cloud-bar", "2.0.0"));
File outputFolder = new TemplateGenerator().generate(projects);
BDDAssertions.then(outputFolder).isNotEmptyDirectory();
}
}

View File

@@ -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.File;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Assumptions;
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
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 repoUrl = System.getProperty("repoUrl");
String unzippedDocs = System.getProperty("unzippedDocs");
String generatedTrainDocs = System.getProperty("generatedTrainDocs");
GenerateReleaseTrainDocs.main(bomPath, repoUrl, unzippedDocs, generatedTrainDocs);
BDDAssertions.then(new File(generatedTrainDocs)).isNotEmptyDirectory();
}
}

View File

@@ -3,7 +3,7 @@
{spring-cloud-version}
Copyright &#169; 2012-2019
Copyright &#169; 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

View File

@@ -1,5 +1,5 @@
<!--
~ Copyright 2013-2019 the original author or authors.
~ 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.