CourseWebsite.groovy
package edu.odu.cs.cowem
import com.moowork.gradle.node.task.NodeTask;
import org.apache.tools.ant.filters.ReplaceTokens
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.bundling.Zip
import org.hidetake.gradle.ssh.plugin.SshPlugin
import edu.odu.cs.cowem.documents.SingleScrollDocument
import edu.odu.cs.cowem.documents.WebsiteProject
/**
* A plugin for describing a course website.
*/
class CourseWebsite implements Plugin<Project> {
void apply (Project project) {
new org.hidetake.gradle.ssh.plugin.SshPlugin().apply(project);
project.getPluginManager().apply(com.moowork.gradle.node.NodePlugin.class);
// Add a Course object as a property of the project
project.extensions.create ("course", Course);
project.extensions.create ("website", WebsiteProject, project.rootDir);
project.remotes {
remotehost {
// Values will be filled in from course settings
host = 'placeholder'
user = 'placeHolder'
agent = true
}
}
project.allprojects {
defaultTasks 'build'
repositories {
// jcenter()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
// Use my own CS dept repo
ivy {
url 'https://www.cs.odu.edu/~zeil/ivyrepo'
}
}
}
project.configurations {
build
deploy
}
project.task('setup_cowem', type: Copy) {
//dependsOn project.configurations.setup
into project.file('build/temp/cowem')
from ({ project.zipTree(project.buildscript.configurations.classpath.find {
it.name.startsWith("cowem-plugin") }
) }) {
include 'edu/odu/cs/cowem/core/graphics/**'
include 'edu/odu/cs/cowem/core/styles/**'
}
}
project.task ('setup_copy_website_defaults',
type: Copy, dependsOn: 'setup_cowem'
) {
from 'build/temp/cowem/edu/odu/cs/cowem/core'
into 'build/website'
include 'graphics/**'
include 'styles/**'
}
project.task ('setup_copy_graphics_overrides',
type: Copy, dependsOn: 'setup_copy_website_defaults'
) {
from 'graphics'
into 'build/website/graphics'
}
project.task ('setup_copy_styles_overrides',
type: Copy, dependsOn: 'setup_copy_website_defaults'
) {
from 'styles'
into 'build/website/styles'
}
project.task ('setup_save_document_map',
dependsOn: 'setup_copy_website_defaults'
) {
doLast {
def json = project.website.getDocumentMap();
File jsonFile = project.file("build/website/styles/documentMap.json");
jsonFile.write (json);
project.website.setImports(project.course.imports);
project.website.setCourseName(project.course.courseName);
}
}
project.task ('setup_copy_index_overrides',
dependsOn: 'setup_copy_styles_overrides'
) {
doLast {
if (project.file('index.html').exists()) {
project.copy {
from 'index.html'
into 'build/website/'
}
} else {
project.copy {
from 'build/website/styles/'
include 'homeRedirect.html'
into 'build/website/'
rename '.+', 'index.html'
}
}
}
}
project.task ('setup_website',
dependsOn: ['setup_copy_index_overrides',
'setup_copy_graphics_overrides',
'setup_copy_styles_overrides',
'setup_save_document_map'])
project.task ("setup") {
// description 'Prepare output and support directories'
dependsOn project.setup_cowem, project.setup_website
}
project.task ('build', dependsOn: 'setup') {
description 'Process documents and course outline to prepare the basic course website.'
group 'Build'
}
project.task ('clean', type: Delete)
{
description 'Clean the project (delete the build directory)'
delete 'build'
//followSymlinks = false
}
project.task ('packages', dependsOn: 'build') {
description 'prepare optional course cartridges'
group 'Packaging'
}
project.task ('zip', type: Zip, dependsOn: 'build') {
description 'Prepare a zip file of the website.'
group 'Packaging'
from 'build/website'
//into '.'
destinationDir = project.file('build/packages')
archiveName 'website.zip'
dirMode 0775
fileMode 0664
includeEmptyDirs true
}
project.task ('singleScroll', dependsOn: 'build') {
description 'Package the website into a single scroll.'
inputs.dir 'build/website'
// outputs.file 'build/combined/scroll-{nbasename}'
} .doLast {
Properties docProperties = new Properties()
docProperties.put('_' + project.rootProject.course.delivery, '1')
project.rootProject.course.properties.each { prop, value ->
docProperties.put(prop, value.toString())
}
project.rootProject.course.ext.properties.each { prop, value ->
docProperties.put(prop, value.toString())
}
String primaryName = "Directory/outline"; // eventually get from Course properties
String primaryDoc = primaryName.substring(primaryName.indexOf('/')+1)
String format = "scroll";
project.delete('build/combined/' + primaryDoc)
SingleScrollDocument doc = new SingleScrollDocument(
project.rootProject.website,
docProperties,
project.file('build/combined/' + primaryDoc),
project.file('build/website/'),
primaryName
);
doc.generate();
}
project.node {
version = '14.15.4' // current LTS version
workDir = project.file("${project.buildDir}/nodejs")
npmWorkDir = project.file("${project.buildDir}")
nodeModulesDir = project.file("${project.buildDir}")
download = true // download node
}
project.build.doLast {
project.copy {
from 'build/website/styles/'
include 'pdf-package.json'
into 'build/'
rename '.+', 'package.json'
}
}
project.npm_install.dependsOn('build')
project.task ('pdfScript', type: Copy, dependsOn: 'build') {
from "build/website/styles/pdfScript.js"
into "build"
filter(ReplaceTokens,
tokens: [scrollFile: project.rootDir.toString().replace("\\", "/") + '/build/combined/outline/index.html'
])
}
project.task ('pdf', type: NodeTask,
dependsOn: ['npm_install', 'singleScroll', 'pdfScript']) {
script = project.file('build/pdfScript.js')
}
project.pdf.doLast {
project.delete('build/pdfScript.js')
}
project.task ('scorm', dependsOn: 'build') {
description 'Package the website as a SCORM 1.2 package for import into LMSs.'
inputs.dir 'build/website'
// outputs.file 'build/packages/bb-${project.name}.zip'
} .doLast {
new ScormPackage(project,
project.course,
project.file('build')
).generate(project.file('build/packages/scorm-'
+ project.name + '.zip'))
}
project.task ('bb', dependsOn: 'build') {
description 'Package the website for import into Blackboard.'
inputs.dir 'build/website'
// outputs.file 'build/packages/bb-${project.name}.zip'
} .doLast {
new BBPackage(project,
project.course,
project.file('build')
).generate(project.file('build/packages/bb-' + project.name + '.zip'), false)
}
project.task ('bbthin', dependsOn: 'build') {
description 'Create a Blackboard package that will link back to the website content.'
inputs.files (project.fileTree('Directory').include('**/*.md'))
outputs.file 'build/packages/bbthin-${project.name}.zip'
} .doLast {
new BBPackage(project,
project.course,
project.file('build')
).generate(project.file('build/packages/bbthin-' + project.name + '.zip'), true)
}
project.task ('deploy', type: Copy, dependsOn: 'build') {
description 'Copy course website to a local deployDestination directory.'
group 'Deployment'
from 'build/website'
into { return project.course.deployDestination; }
dirMode 0775
includeEmptyDirs true
}
project.task ('deployBySsh', dependsOn: 'zip') {
description 'Copy course website to a remote machine.'
group 'Deployment'
inputs.file 'build/packages/website.zip'
} .doLast {
int k0 = project.course.sshDeployURL.indexOf('@')
int k1 = project.course.sshDeployURL.indexOf(':')
def hostName = project.course.sshDeployURL.substring(k0+1,k1)
project.remotes.remotehost.host = hostName
def userName = project.course.sshDeployURL.substring(0, k0)
project.remotes.remotehost.user = userName
def remotePath = project.course.sshDeployURL.substring(k1+1)
if (project.course.sshDeployKey != null) {
File keyFile = project.file(project.course.sshDeployKey)
keyFile.setReadable(false,false)
keyFile.setWritable(false,false)
keyFile.setExecutable(false,false)
keyFile.setReadable(true, true)
keyFile.setWritable(true, true)
project.remotes.remotehost.identity =
project.file(project.course.sshDeployKey)
}
project.ssh.run {
settings {
dryRun = false
}
session (project.remotes.remotehost) {
put from: project.file('build/packages/website.zip'),
into: remotePath
execute "unzip -u -q -o ${remotePath}/website.zip -d ${remotePath}"
execute "/bin/rm -f ${remotePath}/website.zip"
}
println "Sent to " + project.course.sshDeployURL
}
}
project.task ('deployByRsync', dependsOn: 'build') {
// Deloy by rsync requires an external installation of rsync and ssh.
description 'Copy course website to a remote machine by rsync'
group 'Deployment'
inputs.dir 'build/website'
} .doLast {
if (project.course.rsyncDeployURL == null) {
project.course.rsyncDeployURL = project.course.sshDeployURL
if (project.course.rsyncDeployKey == null) {
project.course.rsyncDeployKey = project.course.sshDeployKey
}
}
if (!project.course.rsyncDeployURL.endsWith('/')) {
project.course.rsyncDeployURL = project.course.rsyncDeployURL + '/'
}
def sourceDir = 'build/website/'
String sshCmd = "ssh";
if (project.course.rsyncDeployKey != null) {
File keyFile = project.file(project.course.rsyncDeployKey)
println "keyFile is " + keyFile.toString() + ": setting permissions"
keyFile.setReadable(false,false)
keyFile.setWritable(false,false)
keyFile.setExecutable(false,false)
keyFile.setReadable(true, true)
keyFile.setWritable(true, true)
sshCmd = "ssh -i ${project.course.rsyncDeployKey}"
}
def cmd = [
'rsync',
'-auzv',
'-e' + sshCmd,
sourceDir,
project.course.rsyncDeployURL
]
println ("Issuing rsync command\n" + cmd.iterator().join(" "))
project.exec {
commandLine cmd
if (project.course.rsyncDeployKey != null) {
environment ('SSH_AGENT_PID', '')
environment ('SSH_AUTH_SOCK', '')
}
}
}
project.task ('publish', dependsOn: 'build') {
}
project.task('listProperties') .doLast {
println "All course properties:\n" + project.course.properties.collect{it}.join('\n')
}
}
}