Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ dependencies {
implementation 'org.apache.grails:grails-gradle-plugins'
implementation "org.nosphere.apache.rat:org.nosphere.apache.rat.gradle.plugin:${versions.get('ratVersion')}"
implementation "org.gradle.crypto.checksum:org.gradle.crypto.checksum.gradle.plugin:${versions.get('gradleCryptoChecksumVersion')}"
implementation 'org.apache.grails:grails-docs-core'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package grails.doc.dropdown

import java.nio.file.FileVisitResult
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.SimpleFileVisitor
import java.nio.file.StandardCopyOption
import java.nio.file.attribute.BasicFileAttributes

import javax.inject.Inject

import groovy.transform.CompileStatic

import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction

/**
* Duplicates the documentation and modifies source files to add a release dropdown to the documentation
* @since 6.2.1
*/
@CompileStatic
@CacheableTask
abstract class CreateReleaseDropDownTask extends DefaultTask {

@Input
final Property<String> docBaseUrl

@Input
final Property<String> githubSlug

@InputDirectory
@PathSensitive(PathSensitivity.RELATIVE)
final DirectoryProperty sourceDocsDirectory

@InputFile
@PathSensitive(PathSensitivity.RELATIVE)
final RegularFileProperty gitTags

@Input
final Property<String> projectVersion

@Input
final Property<SoftwareVersion> minimumVersion

@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
final ConfigurableFileCollection filesToAddDropdowns

@OutputDirectory
final DirectoryProperty modifiedPagesDirectory

@Input
final Property<String> versionHtml

@Input
final Property<String> additionalPath

@Inject
CreateReleaseDropDownTask(ObjectFactory objects, Project project) {
group = 'documentation'
githubSlug = objects.property(String).convention(project.provider {
project.findProperty('githubSlug') as String ?: 'apache/grails-core'
})
sourceDocsDirectory = objects.directoryProperty().convention(project.layout.buildDirectory.dir('manual'))
projectVersion = objects.property(String).convention(project.provider { project.version as String })
filesToAddDropdowns = objects.fileCollection()
modifiedPagesDirectory = objects.directoryProperty().convention(project.layout.buildDirectory.dir('modified-pages'))
gitTags = objects.fileProperty().convention(project.layout.buildDirectory.file('git-tags.txt'))
minimumVersion = objects.property(SoftwareVersion).convention(new SoftwareVersion(major: 5))
docBaseUrl = objects.property(String).convention('https://grails.apache.org/docs')
versionHtml = objects.property(String).convention(project.provider { '<p><strong>Version:</strong> ' + projectVersion.get() + '</p>' })
additionalPath = objects.property(String).convention('')
}

/**
* Add the release dropdown to the documentation*/
@TaskAction
void createReleaseDropDown() {
Path targetOutputDirectory = modifiedPagesDirectory.get().asFile.toPath()
if (Files.exists(targetOutputDirectory)) {
targetOutputDirectory.deleteDir()
}
Files.createDirectories(targetOutputDirectory)

String projectVersion = projectVersion.get()

final List<String> result = gitTags.get().asFile.readLines()*.trim()
List<SoftwareVersion> softwareVersions = parseSoftwareVersions(projectVersion, result)
logger.lifecycle("Detected Project Version: ${projectVersion} and Software Versions: ${softwareVersions*.versionText.join(',')}")

Path guideDirectory = sourceDocsDirectory.get().asFile.toPath()

Map<String, Path> filesToAddDropdown = filesToAddDropdowns.collectEntries { [it.absolutePath, it.toPath()] }
Files.walkFileTree(guideDirectory, new SimpleFileVisitor<Path>() {

@Override
FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path targetDir = targetOutputDirectory.resolve(guideDirectory.relativize(dir))
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir)
}
FileVisitResult.CONTINUE
}

@Override
FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path targetFile = targetOutputDirectory.resolve(guideDirectory.relativize(file))
String absolutePath = targetFile.toAbsolutePath().toString()
if (filesToAddDropdown.containsKey(absolutePath)) {
String pageRelativePath = guideDirectory.toFile().relativePath(file.toFile())
String selectHtml = select(options(projectVersion, pageRelativePath, softwareVersions))

final String versionWithSelectHtml = "<p><strong>Version:</strong>&nbsp;<span style='display:inline-block;'>${selectHtml}</span></p>"
targetFile.toFile().text = file.text.replace(versionHtml.get(), versionWithSelectHtml)

filesToAddDropdown.remove(absolutePath)
} else {
Files.copy(file, targetFile, StandardCopyOption.REPLACE_EXISTING)
}
FileVisitResult.CONTINUE
}

@Override
FileVisitResult visitFileFailed(Path file, IOException e) throws IOException {
throw new GradleException("Unable to copy file: ${file}", e)
}
})
}

/**
* Generate the options for the select tag.
*
* @param version The current version of the documentation
* @param pageRelativePath The relative path for the page to add the dropdown to
* @param softwareVersions The list of software versions to include in the dropdown
* @return The list of options for the select tag
*/
private List<String> options(String version, String pageRelativePath, List<SoftwareVersion> softwareVersions) {
List<String> options = []

final String snapshotHref = docBaseUrl.get() + '/snapshot/' + additionalPath.get() + pageRelativePath
options << option(snapshotHref, 'SNAPSHOT', version.endsWith('-SNAPSHOT'))

softwareVersions
.forEach { softwareVersion ->
final String versionName = softwareVersion?.versionText
final String href = docBaseUrl.get() + '/' + (versionName?.endsWith('-SNAPSHOT') ? 'snapshot' : versionName) + '/' + additionalPath.get() + pageRelativePath
options << option(href, versionName, version == versionName)
}

options
}

/**
* Generate the select tag
*
* @param options The List of options tags for the select tag
* @return The select tag with the options
*/
private String select(List<String> options) {
String selectHtml = /<select onChange='window.document.location.href=this.options[this.selectedIndex].value;'>/
options.each { option -> selectHtml += option
}
selectHtml += '</select>'
selectHtml
}

/**
* Generate the option tag
*
* @param value The URL to navigate to
* @param text The version to display
* @param selected Whether the option is selected
*
* @return The option tag
*/
private String option(String value, String text, boolean selected = false) {
if (selected) {
return "<option selected='selected' value='${value}'>${text}</option>"
} else {
return "<option value='${value}'>${text}</option>"
}
}

/**
* Parse the software versions from the resultant JSON
*
* @param result List of all tags in the repository.
* @param minimumVersion Minimum SoftwareVersion to include in the list. Default version is 0.0.0
* @return The list of software versions
*/
private List<SoftwareVersion> parseSoftwareVersions(String projectVersion, List<String> tags) {
def minimum = minimumVersion.get()

LinkedHashSet<String> combined = ["v${projectVersion}" as String]
combined.addAll(tags)

combined.findAll { it ==~ /^v\d.*/ }
.collect { it.replace('v', '') }
.collect { SoftwareVersion.build(it) }
.findAll { it >= minimum }
.toSorted()
.unique()
.reverse()
}
}
82 changes: 82 additions & 0 deletions buildSrc/src/main/groovy/grails/doc/dropdown/Snapshot.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package grails.doc.dropdown

class Snapshot implements Comparable<Snapshot>, Serializable {

private static final long serialVersionUID = 1L

private String text

int getMilestoneVersion() {
text.replace('M', '').toInteger()
}

int getReleaseCandidateVersion() {
text.replace('RC', '').toInteger()
}

boolean isBuildSnapshot() {
text.endsWith('BUILD-SNAPSHOT')
}

boolean isReleaseCandidate() {
text.startsWith('RC')
}

boolean isMilestone() {
text.startsWith('M')
}

Snapshot(String text) {
this.text = text
}

@Override
int compareTo(Snapshot o) {

if (this.buildSnapshot && !o.buildSnapshot) {
return 1
} else if (!this.buildSnapshot && o.buildSnapshot) {
return -1
} else if (this.buildSnapshot && o.buildSnapshot) {
return 0
}

if (this.releaseCandidate && !o.releaseCandidate) {
return 1
} else if (!this.releaseCandidate && o.releaseCandidate) {
return -1
} else if (this.releaseCandidate && o.releaseCandidate) {
return this.releaseCandidateVersion <=> o.releaseCandidateVersion
}

if (this.milestone && !o.milestone) {
return 1
} else if (!this.milestone && o.milestone) {
return -1
} else if (this.milestone && o.milestone) {
return this.milestoneVersion <=> o.milestoneVersion
}

return 0
}
}

Loading
Loading