2 minutes to read
collectIncludes
About This Task
This task crawls through your entire project looking for AsciiDoc files with a specific name pattern, then creates a single AsciiDoc file which includes only those files.
When you create modular documentation, most includes are static. For example, the arc42-template has 12 chapters and a master template that includes those 12 chapters.
Normally when you work with dynamic modules like ADRs (Architecture Decision Records) you create those files on the fly.
Maybe not within your /src/docs
folder, but alongside the code file for which you wrote the ADR.
In order to include these files in your documentation, you have to add the file with its whole relative path to one of your AsciiDoc files.
This task will handle it for you!
Just stick to this file-naming pattern ^[A-Za-z]{3,}[-_].*
(begin with at least three letters and a dash/underscore) and this task will collect the file and write it to your build folder.
You only have to include this generated file from within your documentation.
If you provide templates for the documents, those templates are skipped if the name matches the pattern ^.\*[-\_][tT]emplate
[-\_].*
.
The Optional Parameter Configurations
You can configure which files are found by the script be setting the parameters in the Config.groovy file.
collectIncludes = [:]
collectIncludes.with {
fileFilter = "adoc" // define which files are considered. default: "ad|adoc|asciidoc"
minPrefixLength = "3" // define what minimum length the prefix. default: "3"
maxPrefixLength = "3" // define what maximum length the prefix. default: ""
separatorChar = "_" // define the allowed separators after prefix. default: "-_"
cleanOutputFolder = true // should the output folder be emptied before generation? default: false
excludeDirectories = [] // define additional directories that should not be traversed.
}
Example
You have a file called:
/src/java/yourCompany/domain/books/ADR-1-whyWeUseTheAISINInsteadOFISBN.adoc
The task will collect this file and write another file called:
/build/docs/_includes/ADR_includes.adoc
…which will look like this:
include::../../../src/java/yourCompany/domain/books/ADR-1-whyWeUseTheAISINInsteadOFISBN.adoc[]
Obviously, you’ll reap the most benefits if the task has several ADR files to collect. 😎
You can then include these files in your main documentation by using a single include:
include::{targetDir}/docs/_includes/ADR_includes.adoc[]
Source
Show source code of scripts/collectIncludes.gradle
or go directly to GitHub · docToolchain/scripts/collectIncludes.gradle.
import static groovy.io.FileType.*
import static groovy.io.FileVisitResult.*
import java.security.MessageDigest
task collectIncludes(
description: 'collect all ADRs as includes in one file',
group: 'docToolchain'
) {
doFirst {
boolean cleanOutputFolder = config.collectIncludes.cleanOutputFolder?:false
String outputFolder = targetDir + '/_includes'
if (cleanOutputFolder){
delete fileTree(outputFolder)
}
new File(outputFolder).mkdirs()
}
doLast {
//let's search the whole project for files, not only the docs folder
//exclude typical system folders
final defaultExcludedDirectories = [
'.git', '.github', '.idea', '.gradle', '.repo', '.svn', 'build', 'node_modules'
]
//running as subproject? set scandir to main project
String scanDir_save = scanDir
if (project.name!=rootProject.name && scanDir=='.') {
scanDir = project(':').projectDir.path
}
if (docDir.startsWith('.')) {
docDir = file(new File(projectDir, docDir).canonicalPath)
}
logger.info "docToolchain> docDir: ${docDir}"
logger.info "docToolchain> scanDir: ${scanDir}"
if (scanDir.startsWith('.')) {
scanDir = file(new File(docDir, scanDir).canonicalPath)
} else {
scanDir = file(new File(scanDir, "").canonicalPath)
}
logger.info "docToolchain> scanDir: ${scanDir}"
logger.info "docToolchain> includeRoot: ${includeRoot}"
if (includeRoot.startsWith('.')) {
includeRoot = file(new File(docDir, includeRoot).canonicalPath)
}
logger.info "docToolchain> includeRoot: ${includeRoot}"
File sourceFolder = scanDir
println "sourceFolder: " + sourceFolder.canonicalPath
def collections = [:]
String fileFilter = config.collectIncludes.fileFilter?:"ad|adoc|asciidoc"
String minPrefixLength = config.collectIncludes.minPrefixLength?:"3"
String maxPrefixLength = config.collectIncludes.maxPrefixLength?:""
String separatorChar = config.collectIncludes.separatorChar?:"-_"
def extraExcludeDirectories = config.collectIncludes.excludeDirectories?:[]
def excludedDirectories = defaultExcludedDirectories + extraExcludeDirectories
String prefixRegEx = "[A-Za-z]{" + minPrefixLength + "," + maxPrefixLength + "}"
String separatorCharRegEx = "[" + separatorChar + "]"
String fileFilterRegEx = "^" + prefixRegEx + separatorCharRegEx + ".*[.](" + fileFilter + ")\$"
logger.info "considering files with this pattern: " + fileFilterRegEx
sourceFolder.traverse(
type: FILES,
preDir : { if (it.name in excludedDirectories) return SKIP_SUBTREE },
excludeNameFilter: excludedDirectories
) { file ->
if (file.name ==~ fileFilterRegEx) {
String typeRegEx = "^(" + prefixRegEx + ")" + separatorCharRegEx + ".*\$"
def type = file.name.replaceAll(typeRegEx,'\$1').toUpperCase()
if (!collections[type]) {
collections[type] = []
}
logger.info "file: " + file.canonicalPath
def fileName = (file.canonicalPath - scanDir.canonicalPath)[1..-1]
if (file.name ==~ '^.*[Tt]emplate.*$') {
logger.info "ignore template file: " + fileName
} else {
String includeFileRegEx = "^.*" + prefixRegEx + "_includes.adoc\$"
if (file.name ==~ includeFileRegEx) {
logger.info "ignore generated _includes files: " + fileName
} else {
if ( fileName.startsWith('docToolchain') || fileName.replace("\\", "/").matches('^.*/docToolchain/.*$')) {
//ignore docToolchain as submodule
} else {
logger.info "include corrected file: " + fileName
collections[type] << fileName
}
}
}
}
}
println "targetFolder: " + (targetDir - docDir)
logger.info "targetDir - includeRoot: " + (targetDir - includeRoot)
def pathDiff = '../' * ((targetDir - docDir)
.replaceAll('^/','')
.replaceAll('/$','')
.replaceAll("[^/]",'').size()+1)
logger.info "pathDiff: " + pathDiff
collections.each { type, fileNames ->
if (fileNames) {
def outFile = new File(targetDir + '/_includes', type + '_includes.adoc')
logger.info outFile.canonicalPath-sourceFolder.canonicalPath
outFile.write("// this is autogenerated\n")
logger.info "docToolchain> Use Antora integration: ${useAntoraIntegration}"
fileNames.sort().each { fileName ->
if (useAntoraIntegration) {
outFile.append("ifndef::optimize-content[]\n")
outFile.append ("include::../" + pathDiff + scanDir_save + "/" + fileName.replace("\\", "/")+"[]\n")
outFile.append("endif::optimize-content[]\n\n")
outFile.append("ifdef::optimize-content[]\n")
outFile.append ("include::example\$" + fileName.replace("\\", "/").replace("${inputPath}/modules/ROOT/examples/", "")+"[]\n")
outFile.append("endif::optimize-content[]\n\n")
} else {
outFile.append ("include::../" + pathDiff + scanDir_save + "/" + fileName.replace("\\", "/")+"[]\n\n")
}
}
}
}
}
}
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.