23 changed files with 4 additions and 472 deletions
@ -1,208 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2019-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 io.spring.gradle.convention; |
|
||||||
|
|
||||||
import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider; |
|
||||||
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask; |
|
||||||
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension; |
|
||||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin; |
|
||||||
import org.asciidoctor.gradle.jvm.AsciidoctorTask; |
|
||||||
import org.gradle.api.Action; |
|
||||||
import org.gradle.api.Plugin; |
|
||||||
import org.gradle.api.Project; |
|
||||||
import org.gradle.api.artifacts.Configuration; |
|
||||||
import org.gradle.api.artifacts.DependencySet; |
|
||||||
import org.gradle.api.artifacts.dsl.RepositoryHandler; |
|
||||||
import org.gradle.api.file.CopySpec; |
|
||||||
import org.gradle.api.file.FileTree; |
|
||||||
import org.gradle.api.tasks.Sync; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.net.URI; |
|
||||||
import java.time.LocalDate; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Collections; |
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.Callable; |
|
||||||
import java.util.function.Consumer; |
|
||||||
|
|
||||||
/** |
|
||||||
* Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When |
|
||||||
* the plugin is applied: |
|
||||||
* |
|
||||||
* <ul> |
|
||||||
* <li>All warnings are made fatal. |
|
||||||
* <li>A task is created to resolve and unzip our documentation resources (CSS and |
|
||||||
* Javascript). |
|
||||||
* <li>For each {@link AsciidoctorTask} (HTML only): |
|
||||||
* <ul> |
|
||||||
* <li>A configuration named asciidoctorExtensions is ued to add the |
|
||||||
* <a href="https://github.com/spring-io/spring-asciidoctor-extensions#block-switch">block |
|
||||||
* switch</a> extension |
|
||||||
* <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured. |
|
||||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax |
|
||||||
* highlighting, CSS styling, docinfo, etc. |
|
||||||
* </ul> |
|
||||||
* <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF): |
|
||||||
* <ul> |
|
||||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable |
|
||||||
* warnings for references to missing attributes, the year is added as @{code today-year}, |
|
||||||
* etc |
|
||||||
* <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()} |
|
||||||
* is enabled. |
|
||||||
* </ul> |
|
||||||
* </ul> |
|
||||||
* |
|
||||||
* @author Andy Wilkinson |
|
||||||
* @author Rob Winch |
|
||||||
*/ |
|
||||||
public class AsciidoctorConventionPlugin implements Plugin<Project> { |
|
||||||
|
|
||||||
public void apply(Project project) { |
|
||||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> { |
|
||||||
createDefaultAsciidoctorRepository(project); |
|
||||||
makeAllWarningsFatal(project); |
|
||||||
Sync unzipResources = createUnzipDocumentationResourcesTask(project); |
|
||||||
project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> { |
|
||||||
asciidoctorTask.dependsOn(unzipResources); |
|
||||||
configureExtensions(project, asciidoctorTask); |
|
||||||
configureCommonAttributes(project, asciidoctorTask); |
|
||||||
configureOptions(asciidoctorTask); |
|
||||||
asciidoctorTask.baseDirFollowsSourceDir(); |
|
||||||
asciidoctorTask.useIntermediateWorkDir(); |
|
||||||
asciidoctorTask.resources(new Action<CopySpec>() { |
|
||||||
@Override |
|
||||||
public void execute(CopySpec resourcesSpec) { |
|
||||||
resourcesSpec.from(unzipResources); |
|
||||||
resourcesSpec.from(asciidoctorTask.getSourceDir(), new Action<CopySpec>() { |
|
||||||
@Override |
|
||||||
public void execute(CopySpec resourcesSrcDirSpec) { |
|
||||||
// https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523
|
|
||||||
// For now copy the entire sourceDir over so that include files are
|
|
||||||
// available in the intermediateWorkDir
|
|
||||||
// resourcesSrcDirSpec.include("images/**");
|
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
}); |
|
||||||
if (asciidoctorTask instanceof AsciidoctorTask) { |
|
||||||
configureHtmlOnlyAttributes(project, asciidoctorTask); |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private void createDefaultAsciidoctorRepository(Project project) { |
|
||||||
project.getGradle().afterProject(new Action<Project>() { |
|
||||||
@Override |
|
||||||
public void execute(Project project) { |
|
||||||
RepositoryHandler repositories = project.getRepositories(); |
|
||||||
if (repositories.isEmpty()) { |
|
||||||
repositories.mavenCentral(); |
|
||||||
repositories.maven(repo -> { |
|
||||||
repo.setUrl(URI.create("https://repo.spring.io/release")); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
private void makeAllWarningsFatal(Project project) { |
|
||||||
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*"); |
|
||||||
} |
|
||||||
|
|
||||||
private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) { |
|
||||||
Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions"); |
|
||||||
extensionsConfiguration.defaultDependencies(new Action<DependencySet>() { |
|
||||||
@Override |
|
||||||
public void execute(DependencySet dependencies) { |
|
||||||
dependencies.add(project.getDependencies().create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE")); |
|
||||||
} |
|
||||||
}); |
|
||||||
asciidoctorTask.configurations(extensionsConfiguration); |
|
||||||
} |
|
||||||
|
|
||||||
private Sync createUnzipDocumentationResourcesTask(Project project) { |
|
||||||
Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources"); |
|
||||||
documentationResources.getDependencies() |
|
||||||
.add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5")); |
|
||||||
Sync unzipResources = project.getTasks().create("unzipDocumentationResources", |
|
||||||
Sync.class, new Action<Sync>() { |
|
||||||
@Override |
|
||||||
public void execute(Sync sync) { |
|
||||||
sync.dependsOn(documentationResources); |
|
||||||
sync.from(new Callable<List<FileTree>>() { |
|
||||||
@Override |
|
||||||
public List<FileTree> call() throws Exception { |
|
||||||
List<FileTree> result = new ArrayList<>(); |
|
||||||
documentationResources.getAsFileTree().forEach(new Consumer<File>() { |
|
||||||
@Override |
|
||||||
public void accept(File file) { |
|
||||||
result.add(project.zipTree(file)); |
|
||||||
} |
|
||||||
}); |
|
||||||
return result; |
|
||||||
} |
|
||||||
}); |
|
||||||
File destination = new File(project.getBuildDir(), "docs/resources"); |
|
||||||
sync.into(project.relativePath(destination)); |
|
||||||
} |
|
||||||
}); |
|
||||||
return unzipResources; |
|
||||||
} |
|
||||||
|
|
||||||
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) { |
|
||||||
asciidoctorTask.options(Collections.singletonMap("doctype", "book")); |
|
||||||
} |
|
||||||
|
|
||||||
private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { |
|
||||||
Map<String, Object> attributes = new HashMap<>(); |
|
||||||
attributes.put("source-highlighter", "highlight.js"); |
|
||||||
attributes.put("highlightjsdir", "js/highlight"); |
|
||||||
attributes.put("highlightjs-theme", "github"); |
|
||||||
attributes.put("linkcss", true); |
|
||||||
attributes.put("icons", "font"); |
|
||||||
attributes.put("stylesheet", "css/spring.css"); |
|
||||||
asciidoctorTask.getAttributeProviders().add(new AsciidoctorAttributeProvider() { |
|
||||||
@Override |
|
||||||
public Map<String, Object> getAttributes() { |
|
||||||
Object version = project.getVersion(); |
|
||||||
Map<String, Object> attrs = new HashMap<>(); |
|
||||||
if (version != null && version.toString() != Project.DEFAULT_VERSION) { |
|
||||||
attrs.put("revnumber", version); |
|
||||||
} |
|
||||||
return attrs; |
|
||||||
} |
|
||||||
}); |
|
||||||
asciidoctorTask.attributes(attributes); |
|
||||||
} |
|
||||||
|
|
||||||
private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) { |
|
||||||
Map<String, Object> attributes = new HashMap<>(); |
|
||||||
attributes.put("attribute-missing", "warn"); |
|
||||||
attributes.put("icons", "font"); |
|
||||||
attributes.put("idprefix", ""); |
|
||||||
attributes.put("idseparator", "-"); |
|
||||||
attributes.put("docinfo", "shared"); |
|
||||||
attributes.put("sectanchors", ""); |
|
||||||
attributes.put("sectnums", ""); |
|
||||||
attributes.put("today-year", LocalDate.now().getYear()); |
|
||||||
asciidoctorTask.attributes(attributes); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,87 +0,0 @@ |
|||||||
package io.spring.gradle.convention; |
|
||||||
|
|
||||||
import io.spring.gradle.TestKit; |
|
||||||
import org.codehaus.groovy.runtime.ResourceGroovyMethods; |
|
||||||
import org.gradle.testkit.runner.BuildResult; |
|
||||||
import org.junit.jupiter.api.BeforeEach; |
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
import org.junit.jupiter.api.io.TempDir; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.nio.file.Path; |
|
||||||
import java.util.Collections; |
|
||||||
import java.util.List; |
|
||||||
import java.util.zip.ZipEntry; |
|
||||||
import java.util.zip.ZipFile; |
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||||
import static org.gradle.testkit.runner.TaskOutcome.FAILED; |
|
||||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; |
|
||||||
|
|
||||||
public class DocsPluginITest { |
|
||||||
private TestKit testKit; |
|
||||||
|
|
||||||
@BeforeEach |
|
||||||
void setup(@TempDir Path tempDir) { |
|
||||||
this.testKit = new TestKit(tempDir.toFile()); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void buildTriggersDocs() throws Exception { |
|
||||||
BuildResult result = testKit.withProjectResource("samples/docs/simple/") |
|
||||||
.withArguments("build") |
|
||||||
.build(); |
|
||||||
assertThat(result.task(":build").getOutcome()).isEqualTo(SUCCESS); |
|
||||||
assertThat(result.task(":docs").getOutcome()).isEqualTo(SUCCESS); |
|
||||||
assertThat(result.task(":docsZip").getOutcome()).isEqualTo(SUCCESS); |
|
||||||
File zip = new File(testKit.getRootDir(), "build/distributions/simple-1.0.0.BUILD-SNAPSHOT-docs.zip"); |
|
||||||
try (ZipFile file = new ZipFile(zip)) { |
|
||||||
List<? extends ZipEntry> entries = Collections.list(file.entries()); |
|
||||||
assertThat(entries) |
|
||||||
.extracting(ZipEntry::getName) |
|
||||||
.contains("docs/reference/html5/index.html") |
|
||||||
.contains("docs/reference/pdf/simple-reference.pdf"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void asciidocCopiesImages() throws Exception { |
|
||||||
BuildResult result = testKit.withProjectResource("samples/docs/simple/").withArguments("asciidoctor").build(); |
|
||||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS); |
|
||||||
assertThat(new File(testKit.getRootDir(), "build/docs/asciidoc/images")).exists(); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void asciidocDocInfoFromResourcesUsed() throws Exception { |
|
||||||
BuildResult result = this.testKit.withProjectResource("samples/docs/simple/") |
|
||||||
.withArguments("asciidoctor") |
|
||||||
.build(); |
|
||||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS); |
|
||||||
assertThat(ResourceGroovyMethods.getText(new File(testKit.getRootDir(), "build/docs/asciidoc/index.html"))) |
|
||||||
.contains("<script type=\"text/javascript\" src=\"js/tocbot/tocbot.min.js\"></script>"); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void missingAttributeFails() throws Exception { |
|
||||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-attribute/") |
|
||||||
.withArguments(":asciidoctor") |
|
||||||
.buildAndFail(); |
|
||||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void missingInclude() throws Exception { |
|
||||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-include/") |
|
||||||
.withArguments(":asciidoctor") |
|
||||||
.buildAndFail(); |
|
||||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void missingCrossReference() throws Exception { |
|
||||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-cross-reference/") |
|
||||||
.withArguments(":asciidoctor") |
|
||||||
.buildAndFail(); |
|
||||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,6 +0,0 @@ |
|||||||
plugins { |
|
||||||
id 'io.spring.convention.docs' |
|
||||||
id 'java' |
|
||||||
} |
|
||||||
|
|
||||||
version = '1.0.0.BUILD-SNAPSHOT' |
|
||||||
@ -1 +0,0 @@ |
|||||||
rootProject.name = 'simple' |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
= Example Manual |
|
||||||
|
|
||||||
This will fail due to {missing} attribute |
|
||||||
@ -1,6 +0,0 @@ |
|||||||
plugins { |
|
||||||
id 'io.spring.convention.docs' |
|
||||||
id 'java' |
|
||||||
} |
|
||||||
|
|
||||||
version = '1.0.0.BUILD-SNAPSHOT' |
|
||||||
@ -1 +0,0 @@ |
|||||||
rootProject.name = 'missing-include' |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
= Example Manual |
|
||||||
|
|
||||||
This will fail due to <<missing>> cross reference |
|
||||||
@ -1,6 +0,0 @@ |
|||||||
plugins { |
|
||||||
id 'io.spring.convention.docs' |
|
||||||
id 'java' |
|
||||||
} |
|
||||||
|
|
||||||
version = '1.0.0.BUILD-SNAPSHOT' |
|
||||||
@ -1 +0,0 @@ |
|||||||
rootProject.name = 'missing-include' |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
= Example Manual |
|
||||||
|
|
||||||
This will fail due to missing include |
|
||||||
|
|
||||||
include::missing.adoc[] |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
plugins { |
|
||||||
id 'io.spring.convention.docs' |
|
||||||
id 'java' |
|
||||||
} |
|
||||||
|
|
||||||
version = '1.0.0.BUILD-SNAPSHOT' |
|
||||||
|
|
||||||
asciidoctorj { |
|
||||||
attributes \ |
|
||||||
'build-gradle': project.buildFile, |
|
||||||
'sourcedir': project.sourceSets.main.java.srcDirs[0], |
|
||||||
'endpoint-url': 'https://example.org' |
|
||||||
} |
|
||||||
@ -1 +0,0 @@ |
|||||||
rootProject.name = 'simple' |
|
||||||
@ -1 +0,0 @@ |
|||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script> |
|
||||||
|
Before Width: | Height: | Size: 120 KiB |
@ -1,60 +0,0 @@ |
|||||||
= Example Manual |
|
||||||
Doc Writer <doc.writer@example.org> |
|
||||||
2014-09-09 |
|
||||||
:example-caption!: |
|
||||||
ifndef::imagesdir[:imagesdir: images] |
|
||||||
ifndef::sourcedir[:sourcedir: ../java] |
|
||||||
|
|
||||||
This is a user manual for an example project. |
|
||||||
|
|
||||||
== Introduction |
|
||||||
|
|
||||||
This project does something. |
|
||||||
We just haven't decided what that is yet. |
|
||||||
|
|
||||||
== Source Code |
|
||||||
|
|
||||||
[source,java] |
|
||||||
.Java code from project |
|
||||||
---- |
|
||||||
include::{sourcedir}/example/StringUtils.java[tags=contains,indent=0] |
|
||||||
---- |
|
||||||
|
|
||||||
This page was built by the following command: |
|
||||||
|
|
||||||
$ ./gradlew asciidoctor |
|
||||||
|
|
||||||
== Images |
|
||||||
|
|
||||||
[.thumb] |
|
||||||
image::sunset.jpg[scaledwidth=75%] |
|
||||||
|
|
||||||
== Attributes |
|
||||||
|
|
||||||
.Built-in |
|
||||||
asciidoctor-version:: {asciidoctor-version} |
|
||||||
safe-mode-name:: {safe-mode-name} |
|
||||||
docdir:: {docdir} |
|
||||||
docfile:: {docfile} |
|
||||||
imagesdir:: {imagesdir} |
|
||||||
revnumber:: {revnumber} |
|
||||||
|
|
||||||
.Custom |
|
||||||
sourcedir:: {sourcedir} |
|
||||||
endpoint-url:: {endpoint-url} |
|
||||||
|
|
||||||
== Includes |
|
||||||
|
|
||||||
.include::subdir/_b.adoc[] |
|
||||||
==== |
|
||||||
include::subdir/_b.adoc[] |
|
||||||
==== |
|
||||||
|
|
||||||
WARNING: Includes can be tricky! |
|
||||||
|
|
||||||
== build.gradle |
|
||||||
|
|
||||||
[source,groovy] |
|
||||||
---- |
|
||||||
include::{build-gradle}[] |
|
||||||
---- |
|
||||||
@ -1,7 +0,0 @@ |
|||||||
content from _src/docs/asciidoc/subdir/_b.adoc_. |
|
||||||
|
|
||||||
.include::_c.adoc[] |
|
||||||
[example] |
|
||||||
-- |
|
||||||
include::_c.adoc[] |
|
||||||
-- |
|
||||||
@ -1 +0,0 @@ |
|||||||
content from _src/docs/asciidoc/subdir/c.adoc_. |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
package example; |
|
||||||
|
|
||||||
public class StringUtils { |
|
||||||
// tag::contains[]
|
|
||||||
public boolean contains(String haystack, String needle) { |
|
||||||
return haystack.contains(needle); |
|
||||||
} |
|
||||||
// end::contains[]
|
|
||||||
} |
|
||||||
Loading…
Reference in new issue