23 changed files with 4 additions and 472 deletions
@ -1,208 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -1,6 +0,0 @@
|
||||
plugins { |
||||
id 'io.spring.convention.docs' |
||||
id 'java' |
||||
} |
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT' |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
rootProject.name = 'simple' |
||||
@ -1,3 +0,0 @@
@@ -1,3 +0,0 @@
|
||||
= Example Manual |
||||
|
||||
This will fail due to {missing} attribute |
||||
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
plugins { |
||||
id 'io.spring.convention.docs' |
||||
id 'java' |
||||
} |
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT' |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
rootProject.name = 'missing-include' |
||||
@ -1,3 +0,0 @@
@@ -1,3 +0,0 @@
|
||||
= Example Manual |
||||
|
||||
This will fail due to <<missing>> cross reference |
||||
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
plugins { |
||||
id 'io.spring.convention.docs' |
||||
id 'java' |
||||
} |
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT' |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
rootProject.name = 'missing-include' |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
= Example Manual |
||||
|
||||
This will fail due to missing include |
||||
|
||||
include::missing.adoc[] |
||||
@ -1,13 +0,0 @@
@@ -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 @@
@@ -1 +0,0 @@
|
||||
rootProject.name = 'simple' |
||||
@ -1 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -1,7 +0,0 @@
|
||||
content from _src/docs/asciidoc/subdir/_b.adoc_. |
||||
|
||||
.include::_c.adoc[] |
||||
[example] |
||||
-- |
||||
include::_c.adoc[] |
||||
-- |
||||
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
content from _src/docs/asciidoc/subdir/c.adoc_. |
||||
@ -1,9 +0,0 @@
@@ -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