binaries;
15 | private final GuidesDistribution guidesDistribution;
16 |
17 | @Inject
18 | public GuidesInternal(ObjectFactory objectFactory) {
19 | this.publishedGuides = objectFactory.domainObjectContainer(GuideInternal.class, name -> objectFactory.newInstance(GuideInternal.class, name));
20 | this.binaries = objectFactory.domainObjectSet(GuideBinary.class);
21 | this.guidesDistribution = objectFactory.newInstance(GuidesDistribution.class);
22 | }
23 |
24 | @Override
25 | public NamedDomainObjectContainer extends GuideInternal> getPublishedGuides() {
26 | return publishedGuides;
27 | }
28 |
29 | public DomainObjectSet super GuideBinary> getBinaries() {
30 | return binaries;
31 | }
32 |
33 | @Override
34 | public GuidesDistribution getDistribution() {
35 | return guidesDistribution;
36 | }
37 |
38 | /**
39 | * By convention, this is buildDir/working/guides/docs
40 | *
41 | * @return The root directory for all documentation.
42 | */
43 | public abstract DirectoryProperty getDocumentationInstallRoot();
44 |
45 | /**
46 | * By convention, this is buildDir/working/guides/render-samples
47 | *
48 | * @return The root directory for rendered documentation.
49 | */
50 | public abstract DirectoryProperty getRenderedDocumentationRoot();
51 | }
52 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/guides/internal/LegacyGuideDocumentationPlugin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 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.gradle.docs.guides.internal;
18 |
19 | import org.gradle.api.Plugin;
20 | import org.gradle.api.Project;
21 | import org.gradle.docs.DocumentationExtension;
22 | import org.gradle.docs.guides.Guide;
23 |
24 | import static org.gradle.docs.internal.StringUtils.toLowerCamelCase;
25 |
26 | /**
27 | * The guides base plugin provides conventions for all Gradle guides.
28 | *
29 | * Adds the custom attributes to {@link org.asciidoctor.gradle.jvm.AsciidoctorTask} for reference in Asciidoc files:
30 | *
31 | * - {@literal samplescodedir}: The directory containing samples code defined as {@literal "$projectDir/samples/code"}
32 | * - {@literal samplesoutputdir}: The directory containing expected samples output defined as {@literal "$projectDir/samples/output"}
33 | *
34 | */
35 | public class LegacyGuideDocumentationPlugin implements Plugin {
36 | public static final String GUIDE_EXTENSION_NAME = "guide";
37 |
38 | public void apply(Project project) {
39 | project.getPluginManager().apply(GuidesDocumentationPlugin.class);
40 |
41 | addGuidesExtension(project);
42 | }
43 |
44 | private Guide addGuidesExtension(Project project) {
45 | Guide result = project.getExtensions().getByType(DocumentationExtension.class).getGuides().getPublishedGuides().create(toLowerCamelCase(project.getName()));
46 | project.getExtensions().add(Guide.class, GUIDE_EXTENSION_NAME, result);
47 | result.getGuideName().set(project.getName());
48 | result.getDescription().set(result.getDisplayName());
49 | result.getGuideDirectory().set(project.getProjectDir());
50 | result.getPermalink().set(project.getName());
51 | return result;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/guides/internal/TestableAsciidoctorGuideContentBinary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.guides.internal;
18 |
19 | import org.gradle.docs.internal.TestableAsciidoctorContentBinary;
20 |
21 | import javax.inject.Inject;
22 |
23 | public abstract class TestableAsciidoctorGuideContentBinary extends GuideBinary implements TestableAsciidoctorContentBinary {
24 | @Inject
25 | public TestableAsciidoctorGuideContentBinary(String name) {
26 | super(name);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/guides/internal/tasks/GenerateGuidePageAsciidoc.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.guides.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.UncheckedIOException;
5 | import org.gradle.api.file.RegularFileProperty;
6 | import org.gradle.api.provider.MapProperty;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.InputFile;
9 | import org.gradle.api.tasks.OutputFile;
10 | import org.gradle.api.tasks.TaskAction;
11 |
12 | import java.io.BufferedOutputStream;
13 | import java.io.File;
14 | import java.io.FileOutputStream;
15 | import java.io.IOException;
16 | import java.nio.file.Files;
17 | import java.nio.file.Path;
18 | import java.util.Map;
19 |
20 | public abstract class GenerateGuidePageAsciidoc extends DefaultTask {
21 | @Input
22 | public abstract MapProperty getAttributes();
23 |
24 | @InputFile
25 | public abstract RegularFileProperty getIndexFile();
26 |
27 | @OutputFile
28 | public abstract RegularFileProperty getOutputFile();
29 |
30 | @TaskAction
31 | public void generate() {
32 | File outputFile = getOutputFile().get().getAsFile();
33 | Path sourceFile = getIndexFile().get().getAsFile().toPath();
34 |
35 | try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile))) {
36 | bos.write(generateHeaderForGuidePage());
37 | Files.copy(sourceFile, bos);
38 | } catch (IOException e) {
39 | throw new UncheckedIOException(e);
40 | }
41 | }
42 |
43 | private byte[] generateHeaderForGuidePage() {
44 | StringBuilder sb = new StringBuilder();
45 | writeAttributes(sb);
46 | return sb.toString().getBytes();
47 | }
48 |
49 | private void writeAttributes(StringBuilder sb) {
50 | Map attributes = getAttributes().get();
51 |
52 | attributes.forEach((key, value) -> sb.append(":").append(key).append(": ").append(value).append("\n"));
53 | sb.append('\n');
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/Asserts.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.Named;
4 |
5 | public class Asserts {
6 | public static void assertNameDoesNotContainsDisallowedCharacters(Named element, String format, Object... args) {
7 | if (element.getName().contains("_") || element.getName().contains("-")) {
8 | throw new IllegalArgumentException(String.format(format, args));
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/BuildDocumentationPlugin.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.Plugin;
4 | import org.gradle.api.Project;
5 | import org.gradle.docs.guides.internal.GuidesDocumentationPlugin;
6 | import org.gradle.docs.samples.internal.SamplesDocumentationPlugin;
7 | import org.gradle.docs.snippets.internal.SnippetsDocumentationPlugin;
8 |
9 | public class BuildDocumentationPlugin implements Plugin {
10 | @Override
11 | public void apply(Project project) {
12 | project.getPluginManager().apply(GuidesDocumentationPlugin.class);
13 | project.getPluginManager().apply(SamplesDocumentationPlugin.class);
14 | project.getPluginManager().apply(SnippetsDocumentationPlugin.class);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/DocumentationBasePlugin.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.Plugin;
4 | import org.gradle.api.Project;
5 | import org.gradle.api.Task;
6 | import org.gradle.api.file.ProjectLayout;
7 | import org.gradle.api.tasks.SourceSet;
8 | import org.gradle.api.tasks.SourceSetContainer;
9 | import org.gradle.api.tasks.TaskContainer;
10 | import org.gradle.api.tasks.TaskProvider;
11 | import org.gradle.api.tasks.testing.Test;
12 | import org.gradle.language.base.plugins.LifecycleBasePlugin;
13 |
14 | public class DocumentationBasePlugin implements Plugin {
15 | public static final String DOCUMENTATION_GROUP_NAME = "Documentation";
16 | public static final String DOCUMENTATION_EXTENSION_NAME = "documentation";
17 | public static final String DOCS_TEST_SOURCE_SET_NAME = "docsTest";
18 | public static final String DOCS_TEST_TASK_NAME = "docsTest";
19 | public static final String DOCS_TEST_IMPLEMENTATION_CONFIGURATION_NAME = "docsTestImplementation";
20 |
21 | @Override
22 | public void apply(Project project) {
23 | TaskContainer tasks = project.getTasks();
24 | ProjectLayout layout = project.getLayout();
25 |
26 | project.getPluginManager().apply("base");
27 | project.getExtensions().create(DOCUMENTATION_EXTENSION_NAME, DocumentationExtensionInternal.class);
28 |
29 | TaskProvider check = tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME);
30 |
31 | // Testing
32 | configureTesting(project, layout, tasks, check);
33 | }
34 |
35 | private void configureTesting(Project project, ProjectLayout layout, TaskContainer tasks, TaskProvider check) {
36 | project.getPluginManager().apply("groovy-base");
37 |
38 | SourceSet sourceSet = project.getExtensions().getByType(SourceSetContainer.class).create(DOCS_TEST_SOURCE_SET_NAME);
39 |
40 | TaskProvider docsTestTask = tasks.register(sourceSet.getName(), Test.class, task -> {
41 | task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
42 | task.setDescription("Test documentation");
43 | task.setTestClassesDirs(sourceSet.getRuntimeClasspath());
44 | task.setClasspath(sourceSet.getRuntimeClasspath());
45 | task.setWorkingDir(layout.getProjectDirectory().getAsFile());
46 | });
47 |
48 | check.configure(task -> task.dependsOn(docsTestTask));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/DocumentationExtensionInternal.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.Action;
4 | import org.gradle.api.model.ObjectFactory;
5 | import org.gradle.docs.DocumentationExtension;
6 | import org.gradle.docs.guides.Guides;
7 | import org.gradle.docs.guides.internal.GuidesInternal;
8 | import org.gradle.docs.samples.Samples;
9 | import org.gradle.docs.samples.internal.SamplesInternal;
10 | import org.gradle.docs.snippets.Snippets;
11 | import org.gradle.docs.snippets.internal.SnippetsInternal;
12 |
13 | import javax.inject.Inject;
14 |
15 | public abstract class DocumentationExtensionInternal implements DocumentationExtension {
16 | private final GuidesInternal guides;
17 | private final SamplesInternal samples;
18 | private final SnippetsInternal snippets;
19 |
20 | @Inject
21 | public DocumentationExtensionInternal(ObjectFactory objectFactory) {
22 | this.guides = objectFactory.newInstance(GuidesInternal.class);
23 | this.samples = objectFactory.newInstance(SamplesInternal.class);
24 | this.snippets = objectFactory.newInstance(SnippetsInternal.class);
25 | }
26 |
27 | @Override
28 | public GuidesInternal getGuides() {
29 | return guides;
30 | }
31 |
32 | @Override
33 | public void guides(Action super Guides> action) {
34 | action.execute(guides);
35 | }
36 |
37 | @Override
38 | public SamplesInternal getSamples() {
39 | return samples;
40 | }
41 |
42 | @Override
43 | public void samples(Action super Samples> action) {
44 | action.execute(samples);
45 | }
46 |
47 | @Override
48 | public SnippetsInternal getSnippets() {
49 | return snippets;
50 | }
51 |
52 | @Override
53 | public void snippets(Action super Snippets> action) {
54 | action.execute(snippets);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/FileUtils.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import java.io.File;
4 | import java.nio.file.Files;
5 |
6 | public class FileUtils {
7 | public static void deleteDirectory(File file) {
8 | File[] contents = file.listFiles();
9 | if (contents != null) {
10 | for (File f : contents) {
11 | if (!Files.isSymbolicLink(f.toPath())) {
12 | deleteDirectory(f);
13 | }
14 | }
15 | }
16 | file.delete();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/IOUtils.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.nio.charset.Charset;
7 |
8 | public class IOUtils {
9 | public static String toString(InputStream inStream, Charset encoding) throws IOException {
10 | ByteArrayOutputStream outStream = new ByteArrayOutputStream();
11 | int d;
12 | while ((d = inStream.read()) != -1) {
13 | outStream.write(d);
14 | }
15 |
16 | return outStream.toString(encoding);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/RenderableContentBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.file.ConfigurableFileCollection;
4 | import org.gradle.api.file.CopySpec;
5 | import org.gradle.api.provider.Property;
6 |
7 | public interface RenderableContentBinary {
8 | Property getResourceSpec();
9 |
10 | ConfigurableFileCollection getResourceFiles();
11 |
12 | Property getSourcePattern();
13 | }
14 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/StringUtils.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import java.util.Arrays;
4 | import java.util.regex.Matcher;
5 | import java.util.regex.Pattern;
6 | import java.util.stream.Collectors;
7 |
8 | public class StringUtils {
9 | private static final Pattern UPPER_LOWER = Pattern.compile("(?m)([A-Z]*)([a-z0-9]*)");
10 | /**
11 | * Capitalizes the first letter of the string.
12 | *
13 | * example to Example
14 | *
15 | * @param s the string
16 | * @return transformed string
17 | */
18 | public static String capitalize(String s) {
19 | return Character.toUpperCase(s.charAt(0)) + s.substring(1);
20 | }
21 |
22 | /**
23 | * Makes the first letter of the string lowercase.
24 | *
25 | * Example to example
26 | *
27 | * @param s the string
28 | * @return transformed string
29 | */
30 | public static String uncapitalize(String s) {
31 | return Character.toLowerCase(s.charAt(0)) + s.substring(1);
32 | }
33 |
34 | /**
35 | * Capitalizes every word in a string.
36 | *
37 | * this is an example to This Is An Example
38 | *
39 | * @param s the string
40 | * @return transformed string
41 | */
42 | public static String toTitleCase(String s) {
43 | return Arrays.stream(toWords(s).split(" ")).map(StringUtils::capitalize).collect(Collectors.joining(" "));
44 | }
45 |
46 | /**
47 | * Converts an arbitrary string to space-separated words.
48 | * Eg, camelCase -> camel case, with_underscores -> with underscores
49 | */
50 | private static String toWords(CharSequence string) {
51 | if (string == null) {
52 | return null;
53 | }
54 | char separator = ' ';
55 | StringBuilder builder = new StringBuilder();
56 | int pos = 0;
57 | Matcher matcher = UPPER_LOWER.matcher(string);
58 | while (pos < string.length()) {
59 | matcher.find(pos);
60 | if (matcher.end() == pos) {
61 | // Not looking at a match
62 | pos++;
63 | continue;
64 | }
65 | if (!builder.isEmpty()) {
66 | builder.append(separator);
67 | }
68 | String group1 = matcher.group(1).toLowerCase();
69 | String group2 = matcher.group(2);
70 | if (group2.isEmpty()) {
71 | builder.append(group1);
72 | } else {
73 | if (group1.length() > 1) {
74 | builder.append(group1, 0, group1.length() - 1);
75 | builder.append(separator);
76 | builder.append(group1.substring(group1.length() - 1));
77 | } else {
78 | builder.append(group1);
79 | }
80 | builder.append(group2);
81 | }
82 | pos = matcher.end();
83 | }
84 |
85 | return builder.toString();
86 | }
87 |
88 | /**
89 | * Capitalizes every word in a string, removes all spaces and joins them together as one identifier.
90 | *
91 | * This is like a conventional Java identifier.
92 | *
93 | * this is an example to thisIsAnExample
94 | *
95 | * @param s the string
96 | * @return transformed string
97 | */
98 | public static String toLowerCamelCase(String s) {
99 | return uncapitalize(String.join("", toTitleCase(s).split(" ")));
100 | }
101 |
102 | /**
103 | * Splits a string based on uppercase letters and rejoins them with underscores and makes the identifier lowercase.
104 | *
105 | * ThisIsAnExample to this_is_an_example
106 | *
107 | * @param s the string
108 | * @return transformed string
109 | */
110 | public static String toSnakeCase(String s) {
111 | return s.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
112 | }
113 |
114 | /**
115 | * Like {@link #toSnakeCase(String)}, except separated by hyphens.
116 | *
117 | * ThisIsAnExample to This-is-an-example
118 | *
119 | * @param s the string
120 | * @return transformed string
121 | */
122 | public static String toKebabCase(String s) {
123 | Matcher m = Pattern.compile("(?<=[a-z0-9])[A-Z]").matcher(s);
124 | StringBuilder sb = new StringBuilder();
125 | while (m.find()) {
126 | m.appendReplacement(sb, "-"+m.group().toLowerCase());
127 | }
128 | m.appendTail(sb);
129 | return sb.toString();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/TestableAsciidoctorContentBinary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.internal;
18 |
19 | import org.gradle.api.file.DirectoryProperty;
20 | import org.gradle.api.file.RegularFileProperty;
21 | import org.gradle.docs.guides.internal.GuideContentBinary;
22 | import org.gradle.docs.samples.internal.SampleArchiveBinary;
23 | import org.gradle.docs.samples.internal.SampleContentBinary;
24 |
25 | public interface TestableAsciidoctorContentBinary {
26 | /**
27 | * @return Linked to {@link SampleContentBinary#getInstalledIndexPageFile()} or {@link GuideContentBinary#getInstalledIndexPageFile()}
28 | */
29 | RegularFileProperty getContentFile();
30 |
31 | /**
32 | * @return Linked to {@link SampleArchiveBinary#getInstallDirectory()} or null if no starting sample should be used
33 | */
34 | DirectoryProperty getStartingSampleDirectory();
35 | }
36 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/TestableRenderedContentLinksBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.file.RegularFileProperty;
4 |
5 | public interface TestableRenderedContentLinksBinary {
6 | String getCheckLinksTaskName();
7 |
8 | RegularFileProperty getRenderedPageFile();
9 | }
10 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/ViewableContentBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal;
2 |
3 | import org.gradle.api.file.RegularFileProperty;
4 |
5 | public interface ViewableContentBinary {
6 | String getViewTaskName();
7 |
8 | RegularFileProperty getViewablePageFile();
9 | }
10 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/configure/AsciidoctorTasks.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.configure;
2 |
3 | import groovy.lang.Closure;
4 | import org.asciidoctor.gradle.jvm.AsciidoctorTask;
5 | import org.gradle.api.Action;
6 | import org.gradle.api.Task;
7 | import org.gradle.api.file.CopySpec;
8 | import org.gradle.api.file.DuplicatesStrategy;
9 | import org.gradle.api.tasks.util.PatternSet;
10 | import org.gradle.docs.internal.RenderableContentBinary;
11 |
12 | import java.util.Collection;
13 | import java.util.Collections;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.stream.Collectors;
17 |
18 | import static org.gradle.docs.internal.FileUtils.deleteDirectory;
19 |
20 | public class AsciidoctorTasks {
21 | private static final Object IGNORED_CLOSURE_OWNER = new Object();
22 |
23 | public static void configureResources(AsciidoctorTask task, Collection extends RenderableContentBinary> binaries) {
24 | task.getInputs().files(binaries.stream().map(RenderableContentBinary::getResourceFiles).collect(Collectors.toList())).withPropertyName("resourceFiles").optional(true);
25 | task.resources(new Closure(IGNORED_CLOSURE_OWNER) {
26 | public Object doCall(Object ignore) {
27 | CopySpec copySpec = (CopySpec) this.getDelegate();
28 | copySpec.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE);
29 | binaries.stream()
30 | .map(RenderableContentBinary::getResourceSpec)
31 | .forEach(spec -> copySpec.with(spec.get()));
32 | return null;
33 | }
34 | });
35 | }
36 |
37 | public static void configureSources(AsciidoctorTask task, Collection extends RenderableContentBinary> binaries) {
38 | task.sources(new Closure(IGNORED_CLOSURE_OWNER) {
39 | public Object doCall(Object ignore) {
40 | ((PatternSet)this.getDelegate()).setIncludes(binaries.stream().map(it -> it.getSourcePattern().get()).collect(Collectors.toList()));
41 | return null;
42 | }
43 | });
44 | }
45 |
46 | public static void cleanStaleFiles(AsciidoctorTask task) {
47 | // It seems Asciidoctor task is copying the resource as opposed to synching them. Let's delete the output folder first.
48 | task.doFirst(t -> deleteDirectory(task.getOutputDir()));
49 | }
50 |
51 | public static Map genericAttributes() {
52 | Map attributes = new HashMap<>();
53 | attributes.put("doctype", "book");
54 | attributes.put("icons", "font");
55 | attributes.put("source-highlighter", "prettify");
56 | attributes.put("toc", "auto");
57 | attributes.put("toclevels", 1);
58 | attributes.put("toc-title", "Contents");
59 | return Collections.unmodifiableMap(attributes);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/configure/ContentBinaries.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.configure;
2 |
3 | import org.gradle.api.Project;
4 | import org.gradle.api.Task;
5 | import org.gradle.api.artifacts.Configuration;
6 | import org.gradle.api.artifacts.dsl.DependencyHandler;
7 | import org.gradle.api.tasks.TaskContainer;
8 | import org.gradle.api.tasks.TaskProvider;
9 | import org.gradle.docs.internal.TestableAsciidoctorContentBinary;
10 | import org.gradle.docs.internal.TestableRenderedContentLinksBinary;
11 | import org.gradle.docs.internal.ViewableContentBinary;
12 | import org.gradle.docs.internal.exemplar.AsciidoctorContentTest;
13 | import org.gradle.docs.internal.exemplar.AsciidoctorContentTestConsoleType;
14 | import org.gradle.docs.internal.tasks.CheckLinks;
15 | import org.gradle.docs.internal.tasks.ViewDocumentation;
16 | import org.gradle.docs.samples.internal.SampleContentBinary;
17 | import org.gradle.docs.samples.internal.tasks.GenerateSamplePageAsciidoc;
18 | import org.gradle.language.base.plugins.LifecycleBasePlugin;
19 |
20 | import java.time.Duration;
21 | import java.util.Collection;
22 |
23 | import static org.gradle.docs.internal.DocumentationBasePlugin.DOCUMENTATION_GROUP_NAME;
24 | import static org.gradle.docs.internal.StringUtils.capitalize;
25 |
26 | public class ContentBinaries {
27 | public static void createTasksForSampleContentBinary(TaskContainer tasks, SampleContentBinary binary) {
28 | TaskProvider generateSamplePage = tasks.register("generate" + capitalize(binary.getName()) + "Page", GenerateSamplePageAsciidoc.class, task -> {
29 | task.setDescription("Generates asciidoc page for sample '" + binary.getName() + "'");
30 |
31 | task.getAttributes().put("samples-dir", binary.getSampleInstallDirectory().get().getAsFile().getAbsolutePath());
32 | task.getAttributes().put("gradle-version", binary.getGradleVersion());
33 |
34 | task.getSampleSummary().convention(binary.getSummary());
35 | task.getReadmeFile().convention(binary.getSourcePageFile());
36 | task.getOutputFile().set(
37 | task.getProject().getLayout().getBuildDirectory()
38 | .file(binary.getBaseName().map(fileName -> "tmp/" + fileName + ".adoc")));
39 | });
40 | binary.getIndexPageFile().convention(generateSamplePage.flatMap(GenerateSamplePageAsciidoc::getOutputFile));
41 | }
42 |
43 | public static void createTasksForContentBinary(TaskContainer tasks, ViewableContentBinary binary) {
44 | tasks.register(binary.getViewTaskName(), ViewDocumentation.class, task -> {
45 | task.setGroup(DOCUMENTATION_GROUP_NAME);
46 | task.setDescription("Open in the browser the rendered documentation");
47 | task.getIndexFile().convention(binary.getViewablePageFile());
48 | });
49 | }
50 |
51 | public static void createCheckTasksForContentBinary(TaskContainer tasks, TestableRenderedContentLinksBinary binary, TaskProvider check) {
52 | TaskProvider checkLinksTask = tasks.register(binary.getCheckLinksTaskName(), CheckLinks.class, task -> {
53 | task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
54 | task.setDescription("Check for any dead link in the rendered documentation");
55 | task.getIndexDocument().convention(binary.getRenderedPageFile());
56 | task.getTimeout().convention(Duration.ofMinutes(30));
57 | });
58 |
59 | check.configure(it -> it.dependsOn(checkLinksTask));
60 | }
61 |
62 | public static void createCheckTaskForAsciidoctorContentBinary(Project project, String taskName, Collection extends TestableAsciidoctorContentBinary> binaries, TaskProvider check) {
63 | Configuration configuration = project.getConfigurations().create(taskName);
64 | DependencyHandler dependencies = project.getDependencies();
65 | dependencies.add(configuration.getName(), "org.gradle:gradle-tooling-api:6.0.1");
66 | dependencies.add(configuration.getName(), "org.apache.commons:commons-lang3:3.9");
67 | dependencies.add(configuration.getName(), "org.gradle.exemplar:samples-check:1.0.3");
68 | dependencies.add(configuration.getName(), "junit:junit:4.12");
69 |
70 | TaskProvider asciidoctorContentDocsTest = project.getTasks().register(taskName, AsciidoctorContentTest.class, task -> {
71 | task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
72 | task.setDescription("Check Asciidoctor content steps commands.");
73 | task.getClasspath().from(configuration);
74 | task.getGradleVersion().convention(project.getGradle().getGradleVersion());
75 | task.getDefaultConsoleType().convention(taskName.contains("Sample") ? AsciidoctorContentTestConsoleType.RICH : AsciidoctorContentTestConsoleType.PLAIN);
76 | binaries.forEach(binary -> task.testCase(testCase -> {
77 | testCase.getContentFile().set(binary.getContentFile());
78 | testCase.getStartingSample().set(binary.getStartingSampleDirectory());
79 | }));
80 | });
81 |
82 | check.configure(it -> it.dependsOn(asciidoctorContentDocsTest));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/AnsiCharactersToPlainTextOutputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.internal.exemplar;
18 |
19 | import net.rubygrapefruit.ansi.AnsiParser;
20 | import net.rubygrapefruit.ansi.console.AnsiConsole;
21 | import net.rubygrapefruit.ansi.token.NewLine;
22 | import net.rubygrapefruit.ansi.token.Text;
23 |
24 | import java.io.IOException;
25 | import java.io.OutputStream;
26 |
27 | public class AnsiCharactersToPlainTextOutputStream extends OutputStream {
28 | private final OutputStream delegate;
29 | private final AnsiConsole console;
30 |
31 | public AnsiCharactersToPlainTextOutputStream() {
32 | AnsiParser parser = new AnsiParser();
33 |
34 | this.console = new AnsiConsole();
35 | this.delegate = parser.newParser("utf-8", console);
36 | }
37 |
38 | @Override
39 | public void write(int b) throws IOException {
40 | delegate.write(b);
41 | }
42 |
43 | @Override
44 | public void write(byte[] b) throws IOException {
45 | delegate.write(b);
46 | }
47 |
48 | @Override
49 | public void write(byte[] b, int off, int len) throws IOException {
50 | delegate.write(b, off, len);
51 | }
52 |
53 | @Override
54 | public void flush() throws IOException {
55 | delegate.flush();
56 | }
57 |
58 | @Override
59 | public void close() throws IOException {
60 | delegate.flush();
61 | }
62 |
63 | @Override
64 | public String toString() {
65 | StringBuilder result = new StringBuilder();
66 | console.contents(token -> {
67 | if (token instanceof Text) {
68 | result.append(((Text) token).getText());
69 | } else if (token instanceof NewLine) {
70 | result.append("\n");
71 | }
72 | });
73 | return result.toString();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/AsciidoctorContentTest.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.exemplar;
2 |
3 | import org.gradle.api.Action;
4 | import org.gradle.api.DefaultTask;
5 | import org.gradle.api.file.ConfigurableFileCollection;
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.api.provider.Property;
8 | import org.gradle.api.tasks.Classpath;
9 | import org.gradle.api.tasks.Input;
10 | import org.gradle.api.tasks.Internal;
11 | import org.gradle.api.tasks.Nested;
12 | import org.gradle.api.tasks.Optional;
13 | import org.gradle.api.tasks.TaskAction;
14 | import org.gradle.workers.WorkQueue;
15 | import org.gradle.workers.WorkerExecutor;
16 |
17 | import javax.inject.Inject;
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | public abstract class AsciidoctorContentTest extends DefaultTask {
24 | private final List testCases = new ArrayList<>();
25 |
26 | @Nested
27 | protected List getTestCases() {
28 | return testCases;
29 | }
30 |
31 | public void testCase(Action super AsciidoctorContentTestCase> action) {
32 | AsciidoctorContentTestCase testCase = getObjectFactory().newInstance(AsciidoctorContentTestCase.class);
33 | action.execute(testCase);
34 | testCases.add(testCase);
35 | }
36 |
37 | @Inject
38 | protected ObjectFactory getObjectFactory() {
39 | throw new UnsupportedOperationException();
40 | }
41 |
42 | @Classpath
43 | public abstract ConfigurableFileCollection getClasspath();
44 |
45 | /**
46 | * Note: The default console type doesn't affect the console choice requireing user interaction like `init` task or `--scan` flag
47 | * @return a property for configuring the console type to use as defined by {@link AsciidoctorContentTestConsoleType}
48 | */
49 | @Input @Optional
50 | public abstract Property getDefaultConsoleType();
51 |
52 | @Internal
53 | public abstract Property getGradleVersion();
54 |
55 | @Inject
56 | protected abstract WorkerExecutor getWorkerExecutor();
57 |
58 | @TaskAction
59 | public void doTest() throws IOException {
60 | WorkQueue workQueue = getWorkerExecutor().classLoaderIsolation(spec -> spec.getClasspath().from(getClasspath()));
61 |
62 | workQueue.submit(AsciidoctorContentTestWorkerAction.class, parameter -> {
63 | parameter.getTestCases().set(getTestCases());
64 | parameter.getWorkspaceDirectory().set(getTemporaryDir());
65 | parameter.getGradleUserHomeDirectory().set(new File(getTemporaryDir(), "gradle-user-home"));
66 | parameter.getGradleVersion().set(getGradleVersion());
67 | parameter.getDefaultConsoleType().set(getDefaultConsoleType());
68 | });
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/AsciidoctorContentTestCase.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.exemplar;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.api.file.RegularFileProperty;
5 | import org.gradle.api.tasks.InputDirectory;
6 | import org.gradle.api.tasks.InputFile;
7 | import org.gradle.api.tasks.Optional;
8 |
9 | public interface AsciidoctorContentTestCase {
10 | @InputDirectory @Optional
11 | DirectoryProperty getStartingSample();
12 |
13 | @InputFile
14 | RegularFileProperty getContentFile();
15 | }
16 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/AsciidoctorContentTestConsoleType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.internal.exemplar;
18 |
19 | public enum AsciidoctorContentTestConsoleType {
20 | PLAIN, VERBOSE, RICH
21 | }
22 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/AsciidoctorContentTestParameters.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.exemplar;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.api.provider.ListProperty;
5 | import org.gradle.api.provider.Property;
6 | import org.gradle.workers.WorkParameters;
7 |
8 | public interface AsciidoctorContentTestParameters extends WorkParameters {
9 | ListProperty getTestCases();
10 |
11 | DirectoryProperty getWorkspaceDirectory();
12 |
13 | DirectoryProperty getGradleUserHomeDirectory();
14 |
15 | Property getGradleVersion();
16 |
17 | Property getDefaultConsoleType();
18 | }
19 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/GradleUserHomePathOutputNormalizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.internal.exemplar;
18 |
19 | import org.gradle.exemplar.executor.ExecutionMetadata;
20 | import org.gradle.exemplar.test.normalizer.OutputNormalizer;
21 |
22 | import java.io.File;
23 |
24 | public class GradleUserHomePathOutputNormalizer implements OutputNormalizer {
25 | private final File gradleUserHomeDirectory;
26 |
27 | public GradleUserHomePathOutputNormalizer(File gradleUserHomeDirectory) {
28 | this.gradleUserHomeDirectory = gradleUserHomeDirectory;
29 | }
30 |
31 | @Override
32 | public String normalize(String s, ExecutionMetadata executionMetadata) {
33 | return s.replace(gradleUserHomeDirectory.getAbsolutePath(), "/home/user/.gradle");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/OutputNormalizers.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.exemplar;
2 |
3 | import org.gradle.exemplar.test.normalizer.OutputNormalizer;
4 |
5 | import java.util.function.UnaryOperator;
6 |
7 | public class OutputNormalizers {
8 | @SafeVarargs
9 | public static OutputNormalizer composite(OutputNormalizer... normalizers) {
10 | return (commandOutput, executionMetadata) -> {
11 | for (OutputNormalizer normalizer : normalizers) {
12 | commandOutput = normalizer.normalize(commandOutput, executionMetadata);
13 | }
14 | return commandOutput;
15 | };
16 | }
17 |
18 | public static UnaryOperator toFunctional(OutputNormalizer normalizer) {
19 | return s -> normalizer.normalize(s, null);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/exemplar/UserInputOutputVerifier.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal.exemplar;
2 |
3 | import org.gradle.exemplar.test.verifier.OutputVerifier;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | import static org.junit.Assert.assertTrue;
9 | import static org.junit.Assert.fail;
10 |
11 | public class UserInputOutputVerifier implements OutputVerifier {
12 | private final List actualUserInputs;
13 |
14 | public UserInputOutputVerifier(List actualUserInputs) {
15 | this.actualUserInputs = actualUserInputs;
16 | }
17 |
18 | public void verify(String expected, String actual, boolean allowAdditionalOutput) {
19 | List expectedLines = Arrays.asList(expected.split("\\r?\\n"));
20 | List actualLines = Arrays.asList(actual.split("\\r?\\n"));
21 | int expectedIndex = 0;
22 | int actualIndex = 0;
23 | int userInputIndex = 0;
24 | if (allowAdditionalOutput) {
25 | actualIndex = this.findFirstMatchingLine(actualLines, expectedLines.get(expectedIndex));
26 | }
27 |
28 | while(actualIndex < actualLines.size() && expectedIndex < expectedLines.size()) {
29 | String expectedLine = expectedLines.get(expectedIndex);
30 | String actualLine = actualLines.get(actualIndex);
31 | if (isAskingQuestionToUser(expectedLine, actualLine)) {
32 | String expectedUserInput = expectedLine.substring(actualLine.length()).trim();
33 |
34 | if (expectedUserInput.equals(actualUserInputs.get(userInputIndex))) {
35 | userInputIndex++;
36 |
37 | // Ensure the new line is empty demonstrating an user input
38 | assertTrue(expectedLines.get(++expectedIndex).isEmpty());
39 | } else {
40 | fail(String.format("Unexpected value at line %d.%nExpected: %s%nActual: %s%nActual output:%n%s%n", actualIndex + 1, expectedLine, actualLine, actual));
41 | }
42 | } else if (!expectedLine.equals(actualLine)) {
43 | fail(String.format("Unexpected value at line %d.%nExpected: %s%nActual: %s%nActual output:%n%s%n", actualIndex + 1, expectedLine, actualLine, actual));
44 | }
45 |
46 | ++actualIndex;
47 | ++expectedIndex;
48 | }
49 |
50 | if (actualIndex == actualLines.size() && expectedIndex < expectedLines.size()) {
51 | fail(String.format("Lines missing from actual result, starting at expected line %d.%nExpected: %s%nActual output:%n%s%n", expectedIndex, expectedLines.get(expectedIndex), actual));
52 | }
53 |
54 | if (!allowAdditionalOutput && actualIndex < actualLines.size() && expectedIndex == expectedLines.size()) {
55 | fail(String.format("Extra lines in actual result, starting at line %d.%nActual: %s%nActual output:%n%s%n", actualIndex + 1, actualLines.get(actualIndex), actual));
56 | }
57 |
58 | }
59 |
60 | private boolean isAskingQuestionToUser(String expectedLine, String actualLine) {
61 | // NOTE: This is very opinionated to build init user interaction.
62 | if ((actualLine.startsWith("Enter selection") || actualLine.startsWith("Project name") || actualLine.startsWith("Source package")) && expectedLine.startsWith(actualLine)) {
63 | return true;
64 |
65 | // NOTE: This will detect user input where expected lines contains an explicit user input. User input that use the default suggestion will not be detected and requires the previous, opinionated, check to be detected.
66 | } else if (!expectedLine.equals(actualLine) && expectedLine.startsWith(actualLine)) {
67 | return true;
68 | }
69 |
70 | // No user input detected
71 | return false;
72 | }
73 |
74 | private int findFirstMatchingLine(List actualLines, String expected) {
75 | for(int index = 0; index < actualLines.size(); ++index) {
76 | if (actualLines.get(index).equals(expected)) {
77 | return index;
78 | }
79 | }
80 |
81 | return actualLines.size();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/tasks/CheckLinks.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 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.gradle.docs.internal.tasks;
18 |
19 | import org.gradle.api.DefaultTask;
20 | import org.gradle.api.GradleException;
21 | import org.gradle.api.file.RegularFileProperty;
22 | import org.gradle.api.logging.Logging;
23 | import org.gradle.api.tasks.InputFile;
24 | import org.gradle.api.tasks.TaskAction;
25 | import org.gradle.workers.WorkAction;
26 | import org.gradle.workers.WorkParameters;
27 | import org.gradle.workers.WorkQueue;
28 | import org.gradle.workers.WorkerExecutor;
29 | import org.jsoup.Jsoup;
30 | import org.jsoup.nodes.Document;
31 | import org.slf4j.Logger;
32 |
33 | import javax.inject.Inject;
34 | import java.io.IOException;
35 | import java.net.HttpURLConnection;
36 | import java.net.URI;
37 | import java.net.URISyntaxException;
38 | import java.net.URLConnection;
39 | import java.util.HashSet;
40 | import java.util.Set;
41 | import java.util.stream.Collectors;
42 |
43 | /**
44 | *
45 | */
46 | public abstract class CheckLinks extends DefaultTask {
47 |
48 | @InputFile
49 | public abstract RegularFileProperty getIndexDocument();
50 |
51 | @Inject
52 | public abstract WorkerExecutor getWorkerExecuter();
53 |
54 | @TaskAction
55 | public void exec() {
56 | WorkQueue queue = getWorkerExecuter().noIsolation();
57 | queue.submit(CheckLinksAction.class, params -> params.getIndexDocument().set(getIndexDocument()));
58 | }
59 |
60 | public interface CheckLinksParameters extends WorkParameters {
61 | RegularFileProperty getIndexDocument();
62 | }
63 |
64 | public static abstract class CheckLinksAction implements WorkAction {
65 | private static final Logger logger = Logging.getLogger(CheckLinksAction.class);
66 |
67 | @Override
68 | public void execute() {
69 | try {
70 | Set failures = new HashSet<>();
71 | URI documentUri = getParameters().getIndexDocument().get().getAsFile().toURI();
72 | URLConnection connection = documentUri.toURL().openConnection();
73 | connection.addRequestProperty("User-Agent", "Non empty");
74 |
75 | String html = new String(connection.getInputStream().readAllBytes());
76 | getAnchors(html).forEach(anchor -> {
77 | if (anchor.isAbsolute()) {
78 | if (anchor.getScheme().startsWith("http")) {
79 | if (!isValid(anchor)) {
80 | failures.add(anchor);
81 | }
82 | } else {
83 | logger.debug("SKIPPED (Not http/s): {}", anchor);
84 | }
85 | } else {
86 | logger.debug("SKIPPED (relative): {}", anchor);
87 | }
88 | });
89 |
90 | if (!failures.isEmpty()) {
91 | throw new GradleException("The following links are broken:\n " + failures.stream().map(URI::toString).collect(Collectors.joining("\n")) + "\n");
92 | }
93 | } catch (IOException e) {
94 | throw new RuntimeException(e);
95 | }
96 | }
97 |
98 | private boolean isValid(URI anchor) {
99 | for (int i=0; i<3; i++) {
100 | try {
101 | HttpURLConnection con = (HttpURLConnection) anchor.toURL().openConnection();
102 | con.setInstanceFollowRedirects(true);
103 | con.setRequestMethod("HEAD");
104 | // Fake being a browser
105 | con.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0");
106 | // timeout in 5 seconds
107 | con.setConnectTimeout(5000);
108 | con.setReadTimeout(5000);
109 | int responseCode = con.getResponseCode();
110 | logger.info("RESPONSE: {} = {}", anchor, responseCode);
111 | return true;
112 | } catch (IOException e) {
113 | logger.error("FAILED: {}", anchor, e);
114 | // https://github.com/gradle/gradle-private/issues/3109
115 | // Server is accessible, but we don't keep sessions
116 | if(e.getMessage().contains("Server redirected too many")) {
117 | return true;
118 | }
119 | }
120 | }
121 | return false;
122 | }
123 | }
124 |
125 | /**
126 | * Extracts all anchor href URIs from an HTML document.
127 | *
128 | * @param html The HTML content to parse
129 | * @return A set of URIs found in anchor tags
130 | */
131 | public static Set getAnchors(String html) {
132 | Document doc = Jsoup.parse(html);
133 |
134 | return doc.select("a[href]").stream()
135 | .map(element -> {
136 | try {
137 | return new URI(element.attr("href"));
138 | } catch (URISyntaxException ex) {
139 | throw new RuntimeException(ex);
140 | }
141 | })
142 | .collect(Collectors.toSet());
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/internal/tasks/ViewDocumentation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 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.gradle.docs.internal.tasks;
18 |
19 | import org.gradle.api.DefaultTask;
20 | import org.gradle.api.file.RegularFileProperty;
21 | import org.gradle.api.specs.Specs;
22 | import org.gradle.api.tasks.InputFile;
23 | import org.gradle.api.tasks.TaskAction;
24 |
25 | import javax.inject.Inject;
26 | import java.awt.*;
27 | import java.io.IOException;
28 |
29 | public abstract class ViewDocumentation extends DefaultTask {
30 |
31 | @InputFile
32 | public abstract RegularFileProperty getIndexFile();
33 |
34 | @Inject
35 | public ViewDocumentation() {
36 | getOutputs().upToDateWhen(Specs.satisfyNone());
37 | }
38 |
39 | @TaskAction
40 | void action() throws IOException {
41 | Desktop.getDesktop().open(getIndexFile().get().getAsFile());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Dsl.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import java.util.Locale;
4 |
5 | public enum Dsl {
6 | GROOVY("Groovy"), KOTLIN("Kotlin");
7 |
8 | private final String displayName;
9 |
10 | Dsl(String displayName) {
11 | this.displayName = displayName;
12 | }
13 |
14 | public String getDisplayName() {
15 | return displayName;
16 | }
17 |
18 | public String getConventionalDirectory() {
19 | return name().toLowerCase(Locale.ENGLISH);
20 | }
21 |
22 | public String getDslLabel() {
23 | return getConventionalDirectory() + "-dsl";
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Sample.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import org.gradle.api.Action;
4 | import org.gradle.api.Named;
5 | import org.gradle.api.file.ConfigurableFileCollection;
6 | import org.gradle.api.file.DirectoryProperty;
7 | import org.gradle.api.file.RegularFileProperty;
8 |
9 | /**
10 | * Represent a sample to be documented. Each sample must contain at least a Groovy or Kotlin DSL sample.
11 | */
12 | public interface Sample extends Named, SampleSummary {
13 | /**
14 | * By convention, this is the sample name off the extension's sample root directory.
15 | *
16 | * @return Property for configuring the sample root directory.
17 | */
18 | DirectoryProperty getSampleDirectory();
19 |
20 | /**
21 | * By Convention, this is README.adoc within the sample directory.
22 | *
23 | * @return Property for configuring the readme file for the sample in Asciidoctor format.
24 | */
25 | RegularFileProperty getReadmeFile();
26 |
27 | /**
28 | * @return Sample content that is shared by all DSLs.
29 | *
30 | * By convention, this is the wrapper files and LICENSE.
31 | */
32 | ConfigurableFileCollection getCommonContent();
33 |
34 | /**
35 | * Configure common content.
36 | *
37 | * @param action configuration action
38 | */
39 | void common(Action super ConfigurableFileCollection> action);
40 |
41 | /**
42 | * By convention, this is the "groovy" directory in the sample directory.
43 | *
44 | * @return Sample content that is used for Groovy DSL.
45 | */
46 | ConfigurableFileCollection getGroovyContent();
47 |
48 | /**
49 | * Configure Groovy content.
50 | *
51 | * @param action configuration action
52 | */
53 | void groovy(Action super ConfigurableFileCollection> action);
54 |
55 | /**
56 | * By convention, this is the "kotlin" directory in the sample directory.
57 | *
58 | * @return Sample content that is used for Kotlin DSL.
59 | */
60 | ConfigurableFileCollection getKotlinContent();
61 |
62 | /**
63 | * Configure Kotlin content.
64 | *
65 | * @param action configuration action
66 | */
67 | void kotlin(Action super ConfigurableFileCollection> action);
68 |
69 | /**
70 | * @return Sample content that is used for Exemplar testing
71 | */
72 | ConfigurableFileCollection getTestsContent();
73 |
74 | /**
75 | * Configure testing content.
76 | *
77 | * @param action configuration action
78 | */
79 | void tests(Action super ConfigurableFileCollection> action);
80 | }
81 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/SampleSummary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import org.gradle.api.provider.Property;
4 | import org.gradle.api.provider.SetProperty;
5 | import org.gradle.api.tasks.Input;
6 |
7 | /**
8 | *
9 | */
10 | public interface SampleSummary {
11 | /**
12 | * @return Property for configuring the sample description. The description is used within the sample index.
13 | */
14 | @Input
15 | Property getDescription();
16 |
17 | /**
18 | * @return Property for configuring the sample display name. The display name is used within the sample index.
19 | */
20 | @Input
21 | Property getDisplayName();
22 |
23 | /**
24 | * @return Property for configuring the sample documentation name. This is the name other parts of the documentation would use to refer to the sample.
25 | */
26 | @Input
27 | Property getSampleDocName();
28 |
29 | /**
30 | * @return Property for configuring the category this sample appears in.
31 | */
32 | @Input
33 | Property getCategory();
34 |
35 | /**
36 | * By convention, this is both Groovy and Kotlin.
37 | * Every sample must have at least one DSL.
38 | *
39 | * @return DSLs that should be expected for this sample.
40 | */
41 | @Input
42 | SetProperty getDsls();
43 |
44 | /**
45 | * By Convention this is true.
46 | *
47 | * @return true if the samples should be listed in the samples index.
48 | */
49 | @Input
50 | Property getPromoted();
51 | }
52 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Samples.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import org.gradle.api.NamedDomainObjectContainer;
4 | import org.gradle.api.file.DirectoryProperty;
5 | import org.gradle.api.file.RegularFileProperty;
6 |
7 | public interface Samples {
8 |
9 | /**
10 | * By convention, this is src/samples
11 | *
12 | * @return The root sample directory.
13 | */
14 | DirectoryProperty getSamplesRoot();
15 |
16 | /**
17 | * By convention, this is src/samples/templates
18 | *
19 | * @return The root template directory.
20 | */
21 | DirectoryProperty getTemplatesRoot();
22 |
23 | /**
24 | * This is an asciidoc file, not the generated HTML.
25 | *
26 | * By convention, this is documentationRoot/index.adoc
27 | *
28 | * @return The generated samples index file.
29 | */
30 | RegularFileProperty getSampleIndexFile();
31 |
32 | /**
33 | * @return Buildable elements of all the available samples.
34 | */
35 | SamplesDistribution getDistribution();
36 |
37 | /**
38 | * @return Container of all published samples. This is the primary configuration point for all samples.
39 | */
40 | NamedDomainObjectContainer extends Sample> getPublishedSamples();
41 |
42 | /**
43 | * @return Container of all templates. Templates are reusable parts of a sample.
44 | */
45 | NamedDomainObjectContainer extends Template> getTemplates();
46 | }
47 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/SamplesDistribution.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import org.gradle.api.file.ConfigurableFileCollection;
4 | import org.gradle.api.file.ConfigurableFileTree;
5 |
6 | /**
7 | * This represents the outputs of the samples section of the documentation.
8 | */
9 | public interface SamplesDistribution {
10 |
11 | /**
12 | * @return the collection of samples extracted as a consumer would see them
13 | */
14 | ConfigurableFileTree getInstalledSamples();
15 |
16 | /**
17 | * @return a collection of samples in zip form
18 | */
19 | ConfigurableFileCollection getZippedSamples();
20 |
21 | /**
22 | * @return Collection of samples ready for testing
23 | */
24 | ConfigurableFileTree getTestedInstalledSamples();
25 |
26 | /**
27 | * This is HTML and resources.
28 | *
29 | * @return collection of rendered documentation
30 | */
31 | ConfigurableFileCollection getRenderedDocumentation();
32 | }
33 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/Template.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples;
2 |
3 | import org.gradle.api.Named;
4 | import org.gradle.api.file.DirectoryProperty;
5 | import org.gradle.api.provider.Property;
6 |
7 | public interface Template extends Named {
8 | /**
9 | * @return Source directory of the template
10 | */
11 | DirectoryProperty getSourceDirectory();
12 |
13 | /**
14 | * @return subdirectory to place the template in
15 | */
16 | Property getTarget();
17 |
18 | /**
19 | * @return destination directory for the template
20 | */
21 | DirectoryProperty getTemplateDirectory();
22 | }
23 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/LegacySamplesDocumentationPlugin.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.Plugin;
4 | import org.gradle.api.Project;
5 | import org.gradle.docs.internal.DocumentationExtensionInternal;
6 | import org.gradle.docs.samples.Samples;
7 |
8 | @SuppressWarnings("UnstableApiUsage")
9 | public class LegacySamplesDocumentationPlugin implements Plugin {
10 | @Override
11 | public void apply(Project project) {
12 | project.getPluginManager().apply(SamplesDocumentationPlugin.class);
13 |
14 | // Register a samples extension to configure published samples
15 | SamplesInternal extension = project.getExtensions().getByType(DocumentationExtensionInternal.class).getSamples();
16 |
17 | project.getExtensions().add(Samples.class, "samples", extension);
18 | extension.getSamplesRoot().set(project.getLayout().getProjectDirectory().dir("src/samples"));
19 | extension.getTemplatesRoot().convention(project.getLayout().getProjectDirectory().dir("src/samples/templates"));
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleArchiveBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.file.RegularFileProperty;
4 | import org.gradle.api.provider.Property;
5 | import org.gradle.docs.samples.Dsl;
6 |
7 | import javax.inject.Inject;
8 |
9 | /**
10 | * Represents a sample tailored for a particular DSL.
11 | */
12 | public abstract class SampleArchiveBinary extends SampleInstallBinary {
13 | @Inject
14 | public SampleArchiveBinary(String name) {
15 | super(name);
16 | }
17 |
18 | /**
19 | * @return The language this sample is written for
20 | */
21 | public abstract Property getDsl();
22 |
23 | /**
24 | * @return Gets the validation report for this sample.
25 | */
26 | public abstract RegularFileProperty getValidationReport();
27 |
28 | /**
29 | * @return A zip containing this sample. This is the primary thing produced by a sample for a given language.
30 | */
31 | public abstract RegularFileProperty getZipFile();
32 | }
33 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.Named;
4 |
5 | import javax.inject.Inject;
6 |
7 | public abstract class SampleBinary implements Named {
8 | private final String name;
9 |
10 | @Inject
11 | public SampleBinary(String name) {
12 | this.name = name;
13 | }
14 |
15 | @Override
16 | public String getName() {
17 | return name;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleContentBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.api.file.RegularFileProperty;
5 | import org.gradle.api.provider.Property;
6 | import org.gradle.docs.internal.RenderableContentBinary;
7 | import org.gradle.docs.internal.TestableRenderedContentLinksBinary;
8 | import org.gradle.docs.internal.ViewableContentBinary;
9 | import org.gradle.docs.samples.SampleSummary;
10 |
11 | import javax.inject.Inject;
12 |
13 | import static org.gradle.docs.internal.StringUtils.capitalize;
14 |
15 | public abstract class SampleContentBinary extends SampleBinary implements ViewableContentBinary, TestableRenderedContentLinksBinary, RenderableContentBinary {
16 | @Inject
17 | public SampleContentBinary(String name) {
18 | super(name);
19 | }
20 |
21 | public abstract Property getDisplayName();
22 |
23 | public abstract DirectoryProperty getSampleDirectory();
24 |
25 | public abstract Property getBaseName();
26 |
27 | public abstract Property getRenderedPermalink();
28 |
29 | public abstract Property getSourcePermalink();
30 |
31 | public abstract RegularFileProperty getIndexPageFile();
32 |
33 | public abstract RegularFileProperty getRenderedIndexPageFile();
34 |
35 | public abstract Property getSummary();
36 |
37 | public abstract DirectoryProperty getSampleInstallDirectory();
38 |
39 | @Override
40 | public String getViewTaskName() {
41 | return "view" + capitalize(getName()) + "Sample";
42 | }
43 |
44 | @Override
45 | public String getCheckLinksTaskName() {
46 | return "check" + capitalize(getName()) + "SampleLinks";
47 | }
48 |
49 | public abstract RegularFileProperty getSourcePageFile();
50 |
51 | public abstract RegularFileProperty getInstalledIndexPageFile();
52 |
53 | public abstract Property getGradleVersion();
54 | }
55 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleExemplarBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.file.ConfigurableFileCollection;
4 | import org.gradle.api.file.DirectoryProperty;
5 | import org.gradle.api.provider.Property;
6 |
7 | import javax.inject.Inject;
8 |
9 | public abstract class SampleExemplarBinary extends SampleBinary {
10 | @Inject
11 | public SampleExemplarBinary(String name) {
12 | super(name);
13 | }
14 |
15 | public abstract DirectoryProperty getTestedInstallDirectory();
16 |
17 | public abstract DirectoryProperty getTestedWorkingDirectory();
18 |
19 | public abstract ConfigurableFileCollection getTestsContent();
20 |
21 | public abstract Property getExplicitSanityCheck();
22 | }
23 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleInstallBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.file.ConfigurableFileCollection;
4 | import org.gradle.api.file.DirectoryProperty;
5 | import org.gradle.api.provider.ListProperty;
6 | import org.gradle.api.provider.Property;
7 |
8 | import javax.inject.Inject;
9 |
10 | public abstract class SampleInstallBinary extends SampleBinary {
11 | @Inject
12 | public SampleInstallBinary(String name) {
13 | super(name);
14 | }
15 |
16 | /**
17 | * @return Working directory used by the plugin to expose an assembled sample to consumers.
18 | */
19 | public abstract DirectoryProperty getWorkingDirectory();
20 |
21 | /**
22 | * @return The documentation page for this sample
23 | */
24 | public abstract Property getSampleLinkName();
25 |
26 | /**
27 | * @return All content to include in the sample.
28 | */
29 | public abstract ConfigurableFileCollection getContent();
30 |
31 | /**
32 | * @return Content that is specific to the DSL language.
33 | */
34 | public abstract ConfigurableFileCollection getDslSpecificContent();
35 |
36 | /**
37 | * @return Exclude patterns for files included in this sample
38 | */
39 | public abstract ListProperty getExcludes();
40 |
41 | /**
42 | * @return A installation directory containing this sample. This can be used to get an installed version of the sample.
43 | */
44 | public abstract DirectoryProperty getInstallDirectory();
45 | }
46 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SampleInternal.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.Action;
4 | import org.gradle.api.Task;
5 | import org.gradle.api.file.ConfigurableFileCollection;
6 | import org.gradle.api.file.DirectoryProperty;
7 | import org.gradle.api.tasks.TaskContainer;
8 | import org.gradle.api.tasks.TaskProvider;
9 | import org.gradle.docs.samples.Sample;
10 |
11 | import javax.inject.Inject;
12 |
13 | import static org.gradle.docs.internal.StringUtils.capitalize;
14 |
15 | public abstract class SampleInternal implements Sample {
16 | private final String name;
17 | private final TaskProvider assemble;
18 | private final TaskProvider check;
19 |
20 | @Inject
21 | public SampleInternal(String name, TaskContainer tasks) {
22 | this.name = name;
23 | this.assemble = tasks.register("assemble" + capitalize(name + "Sample"));
24 | this.check = tasks.register("check" + capitalize(name + "Sample"));
25 | }
26 |
27 | @Override
28 | public String getName() {
29 | return name;
30 | }
31 |
32 | @Override
33 | public void common(Action super ConfigurableFileCollection> action) {
34 | action.execute(getCommonContent());
35 | }
36 | @Override
37 | public void groovy(Action super ConfigurableFileCollection> action) {
38 | action.execute(getGroovyContent());
39 | }
40 | @Override
41 | public void kotlin(Action super ConfigurableFileCollection> action) {
42 | action.execute(getKotlinContent());
43 | }
44 | @Override
45 | public void tests(Action super ConfigurableFileCollection> action) {
46 | action.execute(getTestsContent());
47 | }
48 |
49 | /**
50 | * @return Lifecycle task for assembling this sample.
51 | */
52 | public TaskProvider getAssembleTask() {
53 | return assemble;
54 | }
55 |
56 | /**
57 | * @return Lifecycle task for checking this sample.
58 | */
59 | public TaskProvider getCheckTask() {
60 | return check;
61 | }
62 |
63 | /**
64 | * @return Root installation directory for each DSL.
65 | */
66 | public abstract DirectoryProperty getInstallDirectory();
67 | }
68 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/SamplesInternal.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.DomainObjectSet;
4 | import org.gradle.api.NamedDomainObjectContainer;
5 | import org.gradle.api.file.DirectoryProperty;
6 | import org.gradle.api.model.ObjectFactory;
7 | import org.gradle.docs.samples.Samples;
8 | import org.gradle.docs.samples.SamplesDistribution;
9 |
10 | import javax.inject.Inject;
11 |
12 | public abstract class SamplesInternal implements Samples {
13 | private final NamedDomainObjectContainer publishedSamples;
14 | private final DomainObjectSet binaries;
15 | private final NamedDomainObjectContainer templates;
16 | private final SamplesDistribution distribution;
17 |
18 | @Inject
19 | public SamplesInternal(ObjectFactory objectFactory) {
20 | this.publishedSamples = objectFactory.domainObjectContainer(SampleInternal.class, name -> objectFactory.newInstance(SampleInternal.class, name));
21 | this.binaries = objectFactory.domainObjectSet(SampleBinary.class);
22 | this.templates = objectFactory.domainObjectContainer(TemplateInternal.class, name -> objectFactory.newInstance(TemplateInternal.class, name));
23 | this.distribution = objectFactory.newInstance(SamplesDistribution.class);
24 | }
25 |
26 | @Override
27 | public NamedDomainObjectContainer extends SampleInternal> getPublishedSamples() {
28 | return publishedSamples;
29 | }
30 |
31 | public DomainObjectSet super SampleBinary> getBinaries() {
32 | return binaries;
33 | }
34 |
35 | @Override
36 | public NamedDomainObjectContainer extends TemplateInternal> getTemplates() {
37 | return templates;
38 | }
39 |
40 | @Override
41 | public SamplesDistribution getDistribution() {
42 | return distribution;
43 | }
44 |
45 | /**
46 | * By convention, this is buildDir/working/samples/install
47 | *
48 | * @return The root install directory.
49 | */
50 | public abstract DirectoryProperty getInstallRoot();
51 |
52 | /**
53 | * By convention, this is buildDir/working/samples/testing
54 | *
55 | * @return location of installed samples ready for testing
56 | */
57 | public abstract DirectoryProperty getTestedInstallRoot();
58 |
59 | /**
60 | * By convention, this is buildDir/working/samples/docs
61 | *
62 | * @return The root directory for all documentation.
63 | */
64 | public abstract DirectoryProperty getDocumentationInstallRoot();
65 |
66 | /**
67 | * By convention, this is buildDir/working/samples/render-samples
68 | *
69 | * @return The root directory for rendered documentation.
70 | */
71 | public abstract DirectoryProperty getRenderedDocumentationRoot();
72 | }
73 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/TemplateInternal.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.docs.samples.Template;
5 |
6 | import javax.inject.Inject;
7 | import java.util.concurrent.Callable;
8 |
9 | public abstract class TemplateInternal implements Template, Callable {
10 | private final String name;
11 |
12 | @Inject
13 | public TemplateInternal(String name) {
14 | this.name = name;
15 | }
16 |
17 | @Override
18 | public String getName() {
19 | return name;
20 | }
21 |
22 | @Override
23 | public DirectoryProperty call() throws Exception {
24 | return getTemplateDirectory();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/TestableAsciidoctorSampleContentBinary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.samples.internal;
18 |
19 | import org.gradle.docs.internal.TestableAsciidoctorContentBinary;
20 |
21 | import javax.inject.Inject;
22 |
23 | public abstract class TestableAsciidoctorSampleContentBinary extends SampleBinary implements TestableAsciidoctorContentBinary {
24 | @Inject
25 | public TestableAsciidoctorSampleContentBinary(String name) {
26 | super(name);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/GenerateSampleIndexAsciidoc.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.file.RegularFileProperty;
5 | import org.gradle.api.provider.ListProperty;
6 | import org.gradle.api.tasks.Nested;
7 | import org.gradle.api.tasks.OutputFile;
8 | import org.gradle.api.tasks.TaskAction;
9 | import org.gradle.docs.samples.SampleSummary;
10 |
11 | import java.io.FileNotFoundException;
12 | import java.io.PrintWriter;
13 | import java.util.ArrayList;
14 | import java.util.Collections;
15 | import java.util.Comparator;
16 | import java.util.List;
17 | import java.util.Map;
18 | import java.util.TreeMap;
19 |
20 | public abstract class GenerateSampleIndexAsciidoc extends DefaultTask {
21 | @Nested
22 | public abstract ListProperty getSamples();
23 |
24 | @OutputFile
25 | public abstract RegularFileProperty getOutputFile();
26 |
27 | @TaskAction
28 | public void doGenerate() throws FileNotFoundException {
29 | try (PrintWriter out = new PrintWriter(getOutputFile().get().getAsFile())) {
30 | out.println("= Sample Index");
31 | out.println();
32 |
33 | if (getSamples().get().isEmpty()) {
34 | out.println("No available samples.");
35 | } else {
36 | Map> categorizedSamples = new TreeMap<>();
37 | getSamples().get().forEach(sample -> {
38 | if (sample.getPromoted().get()) {
39 | String category = sample.getCategory().get();
40 | List groupedSamples = categorizedSamples.computeIfAbsent(category, k -> new ArrayList<>());
41 | groupedSamples.add(sample);
42 | }
43 | });
44 |
45 | categorizedSamples.forEach((category, samples) -> {
46 | samples.sort(Comparator.comparing(sample -> sample.getDisplayName().get()));
47 | out.println("== " + category);
48 | out.println();
49 | samples.forEach(sample -> {
50 | String description = sample.getDescription().get();
51 | if (description.isEmpty()) {
52 | out.println(String.format("- <<%s#,%s>>", sample.getSampleDocName().get(), sample.getDisplayName().get()));
53 | } else {
54 | out.println(String.format("- <<%s#,%s>>: %s", sample.getSampleDocName().get(), sample.getDisplayName().get(), description));
55 | }
56 | });
57 | out.println();
58 | });
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/GenerateSamplePageAsciidoc.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.UncheckedIOException;
5 | import org.gradle.api.file.RegularFileProperty;
6 | import org.gradle.api.provider.MapProperty;
7 | import org.gradle.api.provider.Property;
8 | import org.gradle.api.tasks.Input;
9 | import org.gradle.api.tasks.InputFile;
10 | import org.gradle.api.tasks.Nested;
11 | import org.gradle.api.tasks.OutputFile;
12 | import org.gradle.api.tasks.TaskAction;
13 | import org.gradle.docs.samples.Dsl;
14 | import org.gradle.docs.samples.SampleSummary;
15 |
16 | import java.io.BufferedOutputStream;
17 | import java.io.File;
18 | import java.io.FileOutputStream;
19 | import java.io.IOException;
20 | import java.nio.file.Files;
21 | import java.nio.file.Path;
22 | import java.util.Map;
23 | import java.util.Set;
24 | import java.util.TreeSet;
25 |
26 | public abstract class GenerateSamplePageAsciidoc extends DefaultTask {
27 | @Input
28 | public abstract MapProperty getAttributes();
29 |
30 | @Nested
31 | public abstract Property getSampleSummary();
32 |
33 | @InputFile
34 | public abstract RegularFileProperty getReadmeFile();
35 |
36 | @OutputFile
37 | public abstract RegularFileProperty getOutputFile();
38 |
39 | @TaskAction
40 | public void generate() {
41 | // TODO: Add useful information to the readme
42 | File outputFile = getOutputFile().get().getAsFile();
43 | Path sourceFile = getReadmeFile().get().getAsFile().toPath();
44 |
45 | try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile))) {
46 | bos.write(generateHeaderForSamplePage());
47 | Files.copy(sourceFile, bos);
48 | } catch (IOException e) {
49 | throw new UncheckedIOException(e);
50 | }
51 | }
52 |
53 | private byte[] generateHeaderForSamplePage() {
54 | SampleSummary sampleSummary = getSampleSummary().get();
55 | String sampleDisplayName = sampleSummary.getDisplayName().get();
56 | String sampleDocName = sampleSummary.getSampleDocName().get();
57 |
58 | StringBuilder sb = new StringBuilder();
59 | writeAttributes(sb);
60 | sb.append("= ").append(sampleDisplayName).append(" Sample\n\n");
61 | sb.append("[.download]\n");
62 | Set dsls = new TreeSet<>(sampleSummary.getDsls().get());
63 | for (Dsl dsl : dsls) {
64 | sb.append(String.format("- link:zips/%s-%s.zip[icon:download[] %s DSL]\n", sampleDocName, dsl.getDslLabel(), dsl.getDisplayName()));
65 | }
66 | sb.append('\n');
67 | return sb.toString().getBytes();
68 | }
69 |
70 | private void writeAttributes(StringBuilder sb) {
71 | Map attributes = getAttributes().get();
72 |
73 | attributes.forEach((key, value) -> sb.append(":").append(key).append(": ").append(value).append("\n"));
74 | sb.append('\n');
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/GenerateSanityCheckTests.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.file.RegularFileProperty;
5 | import org.gradle.api.tasks.OutputFile;
6 | import org.gradle.api.tasks.TaskAction;
7 |
8 | import java.io.File;
9 | import java.io.FileWriter;
10 | import java.io.IOException;
11 |
12 | public abstract class GenerateSanityCheckTests extends DefaultTask {
13 | @OutputFile
14 | public abstract RegularFileProperty getOutputFile();
15 |
16 | @TaskAction
17 | public void generate() throws IOException {
18 | File outputFile = getOutputFile().get().getAsFile();
19 | StringBuilder sb = new StringBuilder();
20 | sb.append("commands: [{\n");
21 | sb.append(" executable: gradle\n");
22 | sb.append(" args: tasks -q\n");
23 | sb.append("}]\n");
24 | try (FileWriter fw = new FileWriter(outputFile)) {
25 | fw.write(sb.toString());
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/GenerateTestSource.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.file.Directory;
5 | import org.gradle.api.file.DirectoryProperty;
6 | import org.gradle.api.tasks.OutputDirectory;
7 | import org.gradle.api.tasks.TaskAction;
8 |
9 | import java.io.IOException;
10 | import java.nio.file.Files;
11 |
12 | public abstract class GenerateTestSource extends DefaultTask {
13 | @OutputDirectory
14 | public abstract DirectoryProperty getOutputDirectory();
15 |
16 | @TaskAction
17 | public void generate() {
18 | String content = """
19 | //CHECKSTYLE:OFF
20 | package org.gradle.exemplar;
21 |
22 | import org.gradle.exemplar.test.normalizer.FileSeparatorOutputNormalizer;
23 | import org.gradle.exemplar.test.normalizer.JavaObjectSerializationOutputNormalizer;
24 | import org.gradle.exemplar.test.normalizer.GradleOutputNormalizer;
25 | import org.gradle.exemplar.test.runner.GradleSamplesRunner;
26 | import org.gradle.exemplar.test.runner.SamplesOutputNormalizers;
27 | import org.gradle.exemplar.test.runner.SamplesRoot;
28 | import org.junit.runner.RunWith;
29 |
30 | @RunWith(GradleSamplesRunner.class)
31 | @SamplesOutputNormalizers({
32 | JavaObjectSerializationOutputNormalizer.class,
33 | FileSeparatorOutputNormalizer.class,
34 | GradleOutputNormalizer.class
35 | })
36 | public class ExemplarExternalSamplesFunctionalTest {}
37 | """;
38 | try {
39 | Directory sourceDirectory = getOutputDirectory().dir("org/gradle/docs/samples/").get();
40 | sourceDirectory.getAsFile().mkdirs();
41 | Files.write(sourceDirectory.file("ExemplarExternalSamplesFunctionalTest.java").getAsFile().toPath(), content.getBytes());
42 | } catch (IOException e) {
43 | throw new UnsupportedOperationException(e);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/InstallSample.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.file.ConfigurableFileCollection;
5 | import org.gradle.api.file.DirectoryProperty;
6 | import org.gradle.api.file.FileSystemOperations;
7 | import org.gradle.api.file.FileTree;
8 | import org.gradle.api.provider.ListProperty;
9 | import org.gradle.api.provider.Property;
10 | import org.gradle.api.tasks.Input;
11 | import org.gradle.api.tasks.InputFiles;
12 | import org.gradle.api.tasks.Internal;
13 | import org.gradle.api.tasks.Optional;
14 | import org.gradle.api.tasks.OutputDirectory;
15 | import org.gradle.api.tasks.SkipWhenEmpty;
16 | import org.gradle.api.tasks.TaskAction;
17 | import org.gradle.internal.work.WorkerLeaseService;
18 |
19 | import javax.inject.Inject;
20 | import java.util.Collections;
21 |
22 | /**
23 | * Installs the sample's zip to the given directory.
24 | */
25 | public abstract class InstallSample extends DefaultTask {
26 | @InputFiles
27 | @SkipWhenEmpty
28 | protected FileTree getSourceAsTree() {
29 | return getSource().getAsFileTree();
30 | }
31 |
32 | @Internal
33 | public abstract ConfigurableFileCollection getSource();
34 |
35 | @Input
36 | @Optional
37 | public abstract Property getReadmeName();
38 |
39 | @Input
40 | @Optional
41 | public abstract ListProperty getExcludes();
42 |
43 | @OutputDirectory
44 | public abstract DirectoryProperty getInstallDirectory();
45 |
46 | @Inject
47 | public abstract WorkerLeaseService getWorkerLeaseService();
48 |
49 | @Inject
50 | protected abstract FileSystemOperations getFs();
51 |
52 | @TaskAction
53 | public void doInstall() {
54 | // TODO: Use the Worker API instead of releasing lock manually
55 | getWorkerLeaseService().runAsIsolatedTask(() -> {
56 | getFs().sync(spec -> {
57 | spec.from(getSource());
58 | spec.into(getInstallDirectory());
59 | spec.exclude(getExcludes().getOrElse(Collections.emptyList()));
60 | spec.rename(getReadmeName().getOrElse("README"), "README");
61 | });
62 | });
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/SyncWithProvider.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.file.DirectoryProperty;
4 | import org.gradle.api.file.ProjectLayout;
5 | import org.gradle.api.provider.ProviderFactory;
6 | import org.gradle.api.tasks.OutputDirectory;
7 | import org.gradle.api.tasks.Sync;
8 | import org.gradle.internal.work.WorkerLeaseService;
9 |
10 | import javax.inject.Inject;
11 |
12 | // TODO: Upstream this
13 | public abstract class SyncWithProvider extends Sync {
14 | @Inject
15 | public SyncWithProvider(ProjectLayout layout, ProviderFactory providers) {
16 | getDestinationDirectory().set(layout.dir(providers.provider(this::getDestinationDir)));
17 | }
18 |
19 | @Inject
20 | public abstract WorkerLeaseService getWorkerLeaseService();
21 |
22 | @Override
23 | protected void copy() {
24 | getWorkerLeaseService().runAsIsolatedTask(super::copy);
25 | }
26 |
27 | @OutputDirectory
28 | public abstract DirectoryProperty getDestinationDirectory();
29 | }
30 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/internal/tasks/ValidateSampleBinary.java:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.samples.internal.tasks;
2 |
3 | import org.gradle.api.DefaultTask;
4 | import org.gradle.api.GradleException;
5 | import org.gradle.api.file.RegularFileProperty;
6 | import org.gradle.api.provider.Property;
7 | import org.gradle.api.tasks.Input;
8 | import org.gradle.api.tasks.InputFile;
9 | import org.gradle.api.tasks.Internal;
10 | import org.gradle.api.tasks.OutputFile;
11 | import org.gradle.api.tasks.TaskAction;
12 | import org.gradle.docs.samples.Dsl;
13 |
14 | import java.io.File;
15 | import java.io.FileNotFoundException;
16 | import java.io.IOException;
17 | import java.io.PrintWriter;
18 | import java.util.Arrays;
19 | import java.util.List;
20 | import java.util.zip.ZipEntry;
21 | import java.util.zip.ZipFile;
22 |
23 | public abstract class ValidateSampleBinary extends DefaultTask {
24 | @InputFile
25 | public abstract RegularFileProperty getZipFile();
26 |
27 | @Input
28 | public abstract Property getDsl();
29 |
30 | @Internal
31 | public abstract Property getSampleName();
32 |
33 | @OutputFile
34 | public abstract RegularFileProperty getReportFile();
35 |
36 | @TaskAction
37 | public void validate() {
38 | // TODO: check for exemplar conf files
39 | Dsl dsl = getDsl().get();
40 | String settingsFileName = getSettingsFileName(dsl);
41 | String name = getSampleName().get();
42 | List requiredContents = Arrays.asList("README", settingsFileName, "gradlew", "gradlew.bat", "gradle/wrapper/gradle-wrapper.jar", "gradle/wrapper/gradle-wrapper.properties");
43 |
44 | // Does the Zip look correct?
45 | File zipFile = getZipFile().get().getAsFile();
46 | try (ZipFile zip = new ZipFile(zipFile)) {
47 | for (String required : requiredContents) {
48 | assertZipContains(name, dsl, zip, required);
49 | }
50 | } catch (IOException e) {
51 | throw new RuntimeException(e);
52 | }
53 |
54 | // TODO: Would be nice to put the failures in this file too.
55 | File reportFile = getReportFile().get().getAsFile();
56 | try (PrintWriter fw = new PrintWriter(reportFile)) {
57 | fw.println(name + " looks valid.");
58 | } catch (FileNotFoundException e) {
59 | throw new RuntimeException(e);
60 | }
61 | }
62 |
63 | private void assertZipContains(String name, Dsl dsl, ZipFile zip, String file) {
64 | ZipEntry entry = zip.getEntry(file);
65 | if (entry == null) {
66 | throw new GradleException("Sample '" + name + "' for " + dsl.getDisplayName() + " DSL is invalid due to missing '" + file + "' file.");
67 | }
68 | }
69 |
70 | private String getSettingsFileName(Dsl dsl) {
71 | return switch (dsl) {
72 | case GROOVY -> "settings.gradle";
73 | case KOTLIN -> "settings.gradle.kts";
74 | };
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/samples/package-info.java:
--------------------------------------------------------------------------------
1 | @org.gradle.api.NonNullApi
2 | package org.gradle.docs.samples;
3 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/Snippet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets;
18 |
19 | import org.gradle.api.Named;
20 | import org.gradle.api.provider.SetProperty;
21 | import org.gradle.docs.samples.Dsl;
22 |
23 | public interface Snippet extends Named {
24 | /**
25 | * By convention, this is both Groovy and Kotlin.
26 | * Every sample must have at least one DSL.
27 | *
28 | * @return DSLs that should be expected for this sample.
29 | */
30 | SetProperty getDsls();
31 | }
32 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/Snippets.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets;
18 |
19 | import org.gradle.api.NamedDomainObjectContainer;
20 | import org.gradle.api.file.DirectoryProperty;
21 |
22 | public interface Snippets {
23 | /**
24 | * By convention, this is src/docs/snippets
25 | *
26 | * @return The root sample directory.
27 | */
28 | DirectoryProperty getSnippetsRoot();
29 |
30 | /**
31 | * @return Container of all published snippets. This is the primary configuration point for all snippets.
32 | */
33 | NamedDomainObjectContainer extends Snippet> getPublishedSnippets();
34 | }
35 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/internal/SnippetBinary.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets.internal;
18 |
19 | import org.gradle.api.Named;
20 |
21 | import javax.inject.Inject;
22 |
23 | public abstract class SnippetBinary implements Named {
24 | private final String name;
25 |
26 | @Inject
27 | public SnippetBinary(String name) {
28 | this.name = name;
29 | }
30 |
31 | @Override
32 | public String getName() {
33 | return name;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/internal/SnippetInternal.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets.internal;
18 |
19 | import org.gradle.docs.snippets.Snippet;
20 |
21 | import javax.inject.Inject;
22 |
23 | public abstract class SnippetInternal implements Snippet {
24 | private final String name;
25 |
26 | @Inject
27 | public SnippetInternal(String name) {
28 | this.name = name;
29 | }
30 |
31 | @Override
32 | public String getName() {
33 | return name;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/internal/SnippetsDocumentationPlugin.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets.internal;
18 |
19 | import org.gradle.api.Plugin;
20 | import org.gradle.api.Project;
21 | import org.gradle.api.file.ProjectLayout;
22 | import org.gradle.docs.internal.DocumentationBasePlugin;
23 | import org.gradle.docs.internal.DocumentationExtensionInternal;
24 |
25 | import static org.gradle.docs.internal.Asserts.assertNameDoesNotContainsDisallowedCharacters;
26 |
27 | public class SnippetsDocumentationPlugin implements Plugin {
28 | @Override
29 | public void apply(Project project) {
30 | ProjectLayout layout = project.getLayout();
31 |
32 | project.getPluginManager().apply(DocumentationBasePlugin.class);
33 |
34 | // Register a samples extension to configure published samples
35 | SnippetsInternal extension = configureSnippetsExtension(project, layout);
36 |
37 | // Trigger everything by realizing sample container
38 | project.afterEvaluate(p -> realizeSnippets(extension));
39 | }
40 |
41 | private SnippetsInternal configureSnippetsExtension(Project project, ProjectLayout layout) {
42 | SnippetsInternal extension = project.getExtensions().getByType(DocumentationExtensionInternal.class).getSnippets();
43 |
44 | extension.getSnippetsRoot().convention(layout.getProjectDirectory().dir("src/docs/snippets"));
45 |
46 | return extension;
47 | }
48 |
49 | private void realizeSnippets(SnippetsInternal extension) {
50 | // TODO: Project is passed strictly for zipTree method
51 | // TODO: Disallow changes to published samples container after this point.
52 | for (SnippetInternal snippet : extension.getPublishedSnippets()) {
53 | assertNameDoesNotContainsDisallowedCharacters(snippet, "Snippet '%s' has disallowed characters", snippet.getName());
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/main/java/org/gradle/docs/snippets/internal/SnippetsInternal.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 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 | * http://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.gradle.docs.snippets.internal;
18 |
19 | import org.gradle.api.DomainObjectSet;
20 | import org.gradle.api.NamedDomainObjectContainer;
21 | import org.gradle.api.model.ObjectFactory;
22 | import org.gradle.docs.snippets.Snippets;
23 |
24 | import javax.inject.Inject;
25 |
26 | public abstract class SnippetsInternal implements Snippets {
27 | private final NamedDomainObjectContainer publishedSnippets;
28 | private final DomainObjectSet binaries;
29 |
30 | @Inject
31 | public SnippetsInternal(ObjectFactory objectFactory) {
32 | this.publishedSnippets = objectFactory.domainObjectContainer(SnippetInternal.class, name -> objectFactory.newInstance(SnippetInternal.class, name));
33 | this.binaries = objectFactory.domainObjectSet(SnippetBinary.class);
34 | }
35 |
36 | @Override
37 | public NamedDomainObjectContainer extends SnippetInternal> getPublishedSnippets() {
38 | return publishedSnippets;
39 | }
40 |
41 | public DomainObjectSet super SnippetBinary> getBinaries() {
42 | return binaries;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/test/groovy/org/gradle/docs/internal/CheckLinksSpec.groovy:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal
2 |
3 | import org.gradle.docs.internal.tasks.CheckLinks
4 | import spock.lang.Specification
5 | import spock.lang.TempDir
6 |
7 | import java.nio.file.Files
8 | import java.nio.file.Path
9 |
10 | class CheckLinksSpec extends Specification {
11 | @TempDir
12 | Path tempDir
13 |
14 | def "should extract absolute links from HTML"() {
15 | given:
16 | def html = """
17 |
18 |
19 | Example
20 | Gradle
21 |
22 |
23 | """
24 |
25 | when:
26 | def anchors = CheckLinks.getAnchors(html)
27 |
28 | then:
29 | anchors.size() == 2
30 | anchors.contains(new URI("https://example.com"))
31 | anchors.contains(new URI("https://gradle.org"))
32 | }
33 |
34 | def "should extract relative links from HTML"() {
35 | given:
36 | def html = """
37 |
38 |
39 | Relative
40 | Another
41 |
42 |
43 | """
44 |
45 | when:
46 | def anchors = CheckLinks.getAnchors(html)
47 |
48 | then:
49 | anchors.size() == 2
50 | anchors.contains(new URI("/relative/path"))
51 | anchors.contains(new URI("another/path"))
52 | }
53 |
54 | def "should handle empty document"() {
55 | given:
56 | def html = ""
57 |
58 | when:
59 | def anchors = CheckLinks.getAnchors(html)
60 |
61 | then:
62 | anchors.isEmpty()
63 | }
64 |
65 | def "should handle malformed HTML"() {
66 | given:
67 | def html = """
68 |
69 |
70 | Example
71 | Gradle
72 |
73 |
74 | """
75 |
76 | when:
77 | def anchors = CheckLinks.getAnchors(html)
78 |
79 | then:
80 | anchors.size() == 2
81 | anchors.contains(new URI("https://example.com"))
82 | anchors.contains(new URI("https://gradle.org"))
83 | }
84 |
85 | private File createHtmlFile(String content) {
86 | def filePath = tempDir.resolve("test.html")
87 | Files.writeString(filePath, content)
88 | return filePath.toFile()
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/gradle-guides-plugin/src/test/groovy/org/gradle/docs/internal/StringUtilsTest.groovy:
--------------------------------------------------------------------------------
1 | package org.gradle.docs.internal
2 |
3 | import spock.lang.Specification
4 |
5 | class StringUtilsTest extends Specification {
6 | def "capitalize works"() {
7 | expect:
8 | StringUtils.capitalize("abcdef") == "Abcdef"
9 | StringUtils.capitalize("ABCDEF") == "ABCDEF"
10 | StringUtils.capitalize("Abcdef") == "Abcdef"
11 | StringUtils.capitalize("12345") == "12345"
12 | }
13 |
14 | def "uncapitalize works"() {
15 | expect:
16 | StringUtils.uncapitalize("abcdef") == "abcdef"
17 | StringUtils.uncapitalize("ABCDEF") == "aBCDEF"
18 | StringUtils.uncapitalize("Abcdef") == "abcdef"
19 | StringUtils.uncapitalize("12345") == "12345"
20 | }
21 |
22 | def "toTitleCase works"() {
23 | expect:
24 | StringUtils.toTitleCase("this is an example") == "This Is An Example"
25 | StringUtils.toTitleCase("abcdef") == "Abcdef"
26 | StringUtils.toTitleCase("ABCDEF") == "Abcdef"
27 | StringUtils.toTitleCase("Abcdef") == "Abcdef"
28 | StringUtils.toTitleCase("12345") == "12345"
29 | }
30 |
31 | def "toLowerCamelCase works"() {
32 | expect:
33 | StringUtils.toLowerCamelCase("ThisIsAnExample") == "thisIsAnExample"
34 | StringUtils.toLowerCamelCase("abcdef") == "abcdef"
35 | StringUtils.toLowerCamelCase("ABCDEF") == "abcdef"
36 | StringUtils.toLowerCamelCase("Abcdef") == "abcdef"
37 | StringUtils.toLowerCamelCase("12345") == "12345"
38 | }
39 |
40 | def "toSnakeCase works"() {
41 | expect:
42 | StringUtils.toSnakeCase("ThisIsAnExample") == "this_is_an_example"
43 | StringUtils.toSnakeCase("abcdef") == "abcdef"
44 | StringUtils.toSnakeCase("ABCDEF") == "a_bc_de_f"
45 | StringUtils.toSnakeCase("Abcdef") == "abcdef"
46 | StringUtils.toSnakeCase("12345") == "12345"
47 | StringUtils.toSnakeCase("javaJunit4IntegrationTestForListLibrary") == "java_junit4_integration_test_for_list_library"
48 | }
49 |
50 | def "toKebabCase works"() {
51 | expect:
52 | StringUtils.toKebabCase("ThisIsAnExample") == "This-is-an-example"
53 | StringUtils.toKebabCase("abcdef") == "abcdef"
54 | StringUtils.toKebabCase("ABCDEF") == "ABCDEF"
55 | StringUtils.toKebabCase("Abcdef") == "Abcdef"
56 | StringUtils.toKebabCase("12345") == "12345"
57 | StringUtils.toKebabCase("javaJunit4IntegrationTestForListLibrary") == "java-junit4-integration-test-for-list-library"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx2500m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
2 | org.gradle.parallel=true
3 | org.gradle.caching=true
4 | dependency.analysis.print.build.health=true
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gradle/guides/8837671975cf2650b7ed38d16b04f3c703bbd031/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.gradle.develocity").version("3.18.1")
3 | id("com.autonomousapps.build-health").version("2.17.0")
4 | id("io.github.gradle.gradle-enterprise-conventions-plugin").version("0.10.2")
5 | }
6 |
7 | rootProject.name = "gradle-guides"
8 |
9 | include("gradle-guides-plugin")
10 |
--------------------------------------------------------------------------------