Skip to content

Commit f9281a6

Browse files
wilkinsonaphilwebb
andcommitted
Rework Antora Gradle Infrastructure
Closes gh-40572 Co-authored-by: Phillip Webb <[email protected]>
1 parent 6470748 commit f9281a6

File tree

21 files changed

+1128
-283
lines changed

21 files changed

+1128
-283
lines changed

buildSrc/build.gradle

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ gradlePlugin {
7070
id = "org.springframework.boot.annotation-processor"
7171
implementationClass = "org.springframework.boot.build.processors.AnnotationProcessorPlugin"
7272
}
73+
antoraAggregatedPlugin {
74+
id = "org.springframework.boot.antora-contributor"
75+
implementationClass = "org.springframework.boot.build.antora.AntoraContributorPlugin"
76+
}
77+
antoraAggregatorPlugin {
78+
id = "org.springframework.boot.antora-dependencies"
79+
implementationClass = "org.springframework.boot.build.antora.AntoraDependenciesPlugin"
80+
}
7381
architecturePlugin {
7482
id = "org.springframework.boot.architecture"
7583
implementationClass = "org.springframework.boot.build.architecture.ArchitecturePlugin"

buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,18 @@ public class AntoraConventions {
5858

5959
private static final String DEPENDENCIES_PATH = ":spring-boot-project:spring-boot-dependencies";
6060

61-
private static final String ANTORA_SOURCE_DIR = "src/docs/antora";
62-
6361
private static final List<String> NAV_FILES = List.of("nav.adoc", "local-nav.adoc");
6462

63+
/**
64+
* Default Antora source directory.
65+
*/
66+
public static final String ANTORA_SOURCE_DIR = "src/docs/antora";
67+
68+
/**
69+
* Name of the {@link GenerateAntoraPlaybook} task.
70+
*/
71+
public static final String GENERATE_ANTORA_PLAYBOOK_TASK_NAME = "generateAntoraPlaybook";
72+
6573
void apply(Project project) {
6674
project.getPlugins().withType(AntoraPlugin.class, (antoraPlugin) -> apply(project, antoraPlugin));
6775
}
@@ -70,7 +78,7 @@ private void apply(Project project, AntoraPlugin antoraPlugin) {
7078
ExtractVersionConstraints dependencyVersionsTask = addDependencyVersionsTask(project);
7179
project.getPlugins().apply(GenerateAntoraYmlPlugin.class);
7280
TaskContainer tasks = project.getTasks();
73-
GenerateAntoraPlaybook generateAntoraPlaybookTask = tasks.create("generateAntoraPlaybook",
81+
GenerateAntoraPlaybook generateAntoraPlaybookTask = tasks.create(GENERATE_ANTORA_PLAYBOOK_TASK_NAME,
7482
GenerateAntoraPlaybook.class);
7583
configureGenerateAntoraPlaybookTask(project, generateAntoraPlaybookTask);
7684
Copy copyAntoraPackageJsonTask = tasks.create("copyAntoraPackageJson", Copy.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.antora;
18+
19+
import org.gradle.api.Project;
20+
21+
/**
22+
* A contribution of aggregate content.
23+
*
24+
* @author Andy Wilkinson
25+
*/
26+
class AggregateContentContribution extends ConsumableContentContribution {
27+
28+
protected AggregateContentContribution(Project project, String name) {
29+
super(project, "aggregate", name);
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.antora;
18+
19+
import javax.inject.Inject;
20+
21+
import org.antora.gradle.AntoraPlugin;
22+
import org.gradle.api.Action;
23+
import org.gradle.api.NamedDomainObjectContainer;
24+
import org.gradle.api.Plugin;
25+
import org.gradle.api.Project;
26+
import org.gradle.api.file.CopySpec;
27+
28+
/**
29+
* {@link Plugin} for a project that contributes to Antora-based documentation that is
30+
* {@link AntoraDependenciesPlugin depended upon} by another project.
31+
*
32+
* @author Andy Wilkinson
33+
*/
34+
public class AntoraContributorPlugin implements Plugin<Project> {
35+
36+
@Override
37+
public void apply(Project project) {
38+
project.getPlugins().apply(AntoraPlugin.class);
39+
NamedDomainObjectContainer<Contribution> antoraContributions = project.getObjects()
40+
.domainObjectContainer(Contribution.class,
41+
(name) -> project.getObjects().newInstance(Contribution.class, name, project));
42+
project.getExtensions().add("antoraContributions", antoraContributions);
43+
}
44+
45+
public static class Contribution {
46+
47+
private final String name;
48+
49+
private final Project project;
50+
51+
@Inject
52+
public Contribution(String name, Project project) {
53+
this.name = name;
54+
this.project = project;
55+
}
56+
57+
public String getName() {
58+
return this.name;
59+
}
60+
61+
public void source() {
62+
new SourceContribution(this.project, this.name).produce();
63+
}
64+
65+
public void catalogContent(Action<CopySpec> action) {
66+
CopySpec copySpec = this.project.copySpec();
67+
action.execute(copySpec);
68+
new CatalogContentContribution(this.project, this.name).produceFrom(copySpec);
69+
}
70+
71+
public void aggregateContent(Action<CopySpec> action) {
72+
CopySpec copySpec = this.project.copySpec();
73+
action.execute(copySpec);
74+
new AggregateContentContribution(this.project, this.name).produceFrom(copySpec);
75+
}
76+
77+
public void localAggregateContent(Action<CopySpec> action) {
78+
CopySpec copySpec = this.project.copySpec();
79+
action.execute(copySpec);
80+
new LocalAggregateContentContribution(this.project, this.name).produceFrom(copySpec);
81+
}
82+
83+
}
84+
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.antora;
18+
19+
import javax.inject.Inject;
20+
21+
import org.gradle.api.NamedDomainObjectContainer;
22+
import org.gradle.api.Plugin;
23+
import org.gradle.api.Project;
24+
25+
/**
26+
* {@link Plugin} for a project that depends on {@link AntoraContributorPlugin
27+
* contributed} Antora-based documentation.
28+
*
29+
* @author Andy Wilkinson
30+
*/
31+
public class AntoraDependenciesPlugin implements Plugin<Project> {
32+
33+
@Override
34+
public void apply(Project project) {
35+
NamedDomainObjectContainer<AntoraDependency> antoraDependencies = project.getObjects()
36+
.domainObjectContainer(AntoraDependency.class);
37+
project.getExtensions().add("antoraDependencies", antoraDependencies);
38+
}
39+
40+
public static class AntoraDependency {
41+
42+
private final String name;
43+
44+
private final Project project;
45+
46+
private String path;
47+
48+
@Inject
49+
public AntoraDependency(String name, Project project) {
50+
this.name = name;
51+
this.project = project;
52+
}
53+
54+
public String getName() {
55+
return this.name;
56+
}
57+
58+
public String getPath() {
59+
return this.path;
60+
}
61+
62+
public void setPath(String path) {
63+
this.path = path;
64+
}
65+
66+
public void catalogContent() {
67+
new CatalogContentContribution(this.project, this.name).consumeFrom(this.path);
68+
}
69+
70+
public void aggregateContent() {
71+
new AggregateContentContribution(this.project, this.name).consumeFrom(this.path);
72+
}
73+
74+
public void source() {
75+
new SourceContribution(this.project, this.name).consumeFrom(this.path);
76+
}
77+
78+
}
79+
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.antora;
18+
19+
import org.gradle.api.Project;
20+
21+
/**
22+
* A contribution of catalog content.
23+
*
24+
* @author Andy Wilkinson
25+
*/
26+
class CatalogContentContribution extends ConsumableContentContribution {
27+
28+
CatalogContentContribution(Project project, String name) {
29+
super(project, "catalog", name);
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.build.antora;
18+
19+
import org.gradle.api.Project;
20+
import org.gradle.api.Task;
21+
import org.gradle.api.artifacts.Configuration;
22+
import org.gradle.api.artifacts.dsl.DependencyHandler;
23+
import org.gradle.api.file.CopySpec;
24+
import org.gradle.api.file.Directory;
25+
import org.gradle.api.file.RegularFile;
26+
import org.gradle.api.provider.Provider;
27+
import org.gradle.api.publish.PublishingExtension;
28+
import org.gradle.api.publish.maven.MavenPublication;
29+
import org.gradle.api.tasks.TaskContainer;
30+
import org.gradle.api.tasks.TaskProvider;
31+
32+
/**
33+
* A contribution of content to Antora that can be consumed by other projects.
34+
*
35+
* @author Andy Wilkinson
36+
*/
37+
class ConsumableContentContribution extends ContentContribution {
38+
39+
protected ConsumableContentContribution(Project project, String type, String name) {
40+
super(project, name, type);
41+
}
42+
43+
@Override
44+
void produceFrom(CopySpec copySpec) {
45+
TaskProvider<? extends Task> producer = super.configureProduction(copySpec);
46+
Configuration configuration = createConfiguration(getName(),
47+
"Configuration for %s Antora %s content artifacts.");
48+
configuration.setCanBeConsumed(true);
49+
configuration.setCanBeResolved(false);
50+
getProject().getArtifacts().add(configuration.getName(), producer);
51+
}
52+
53+
void consumeFrom(String path) {
54+
Configuration configuration = createConfiguration(getName(), "Configuration for %s Antora %s content.");
55+
configuration.setCanBeConsumed(false);
56+
configuration.setCanBeResolved(true);
57+
DependencyHandler dependencies = getProject().getDependencies();
58+
dependencies.add(configuration.getName(),
59+
getProject().provider(() -> projectDependency(path, configuration.getName())));
60+
Provider<Directory> outputDirectory = outputDirectory("content", getName());
61+
TaskContainer tasks = getProject().getTasks();
62+
TaskProvider<?> copyAntoraContent = tasks.register(taskName("copy", "%s", configuration.getName()),
63+
CopyAntoraContent.class, (task) -> configureCopyContent(task, path, configuration, outputDirectory));
64+
configureAntora(addInputFrom(copyAntoraContent, configuration.getName()));
65+
configurePlaybookGeneration(this::addToZipContentsCollectorDependencies);
66+
getProject().getExtensions()
67+
.getByType(PublishingExtension.class)
68+
.getPublications()
69+
.withType(MavenPublication.class)
70+
.configureEach((mavenPublication) -> addPublishedMavenArtifact(mavenPublication, copyAntoraContent));
71+
}
72+
73+
private void configureCopyContent(CopyAntoraContent task, String path, Configuration configuration,
74+
Provider<Directory> outputDirectory) {
75+
task.setDescription(
76+
"Syncs the %s Antora %s content from %s.".formatted(getName(), toDescription(getType()), path));
77+
task.setSource(configuration);
78+
task.getOutputFile().set(outputDirectory.map(this::getContentZipFile));
79+
}
80+
81+
private void addToZipContentsCollectorDependencies(GenerateAntoraPlaybook task) {
82+
task.getAntoraExtensions().getZipContentsCollector().getDependencies().add(getName());
83+
}
84+
85+
private void addPublishedMavenArtifact(MavenPublication mavenPublication, TaskProvider<?> copyAntoraContent) {
86+
if ("maven".equals(mavenPublication.getName())) {
87+
String classifier = "%s-%s-content".formatted(getName(), getType());
88+
mavenPublication.artifact(copyAntoraContent, (mavenArtifact) -> mavenArtifact.setClassifier(classifier));
89+
}
90+
}
91+
92+
private RegularFile getContentZipFile(Directory dir) {
93+
Object version = getProject().getVersion();
94+
return dir.file("spring-boot-docs-%s-%s-%s-content.zip".formatted(version, getName(), getType()));
95+
}
96+
97+
private static String toDescription(String input) {
98+
return input.replace("-", " ");
99+
}
100+
101+
private Configuration createConfiguration(String name, String description) {
102+
return getProject().getConfigurations()
103+
.create(configurationName(name, "Antora%sContent", getType()),
104+
(configuration) -> configuration.setDescription(description.formatted(getName(), getType())));
105+
}
106+
107+
}

0 commit comments

Comments
 (0)