JAL-4034 add round corners to 3DB confirmation button with icon on a mac
[jalview.git] / build.gradle
index 76ccefd..4f9798c 100644 (file)
@@ -26,6 +26,8 @@ import com.vladsch.flexmark.ext.toc.TocExtension
 import com.google.common.hash.HashCode
 import com.google.common.hash.Hashing
 import com.google.common.io.Files
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
 
 buildscript {
   repositories {
@@ -34,6 +36,7 @@ buildscript {
   }
   dependencies {
     classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
+    classpath "org.jsoup:jsoup:1.14.3"
   }
 }
 
@@ -45,10 +48,8 @@ plugins {
   id "com.diffplug.gradle.spotless" version "3.28.0"
   id 'com.github.johnrengelman.shadow' version '4.0.3'
   id 'com.install4j.gradle' version '9.0.6'
-  id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
+  id 'com.dorongold.task-tree' version '2.1.0' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
   id 'com.palantir.git-version' version '0.13.0' apply false
-//  id 'org.gradle.crypto.checksum' version '1.4.0'
-
 }
 
 repositories {
@@ -137,6 +138,7 @@ ext {
   if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
     JALVIEW_VERSION = releaseProps.get("jalview.version")
   }
+  println("JALVIEW_VERSION is set to '${JALVIEW_VERSION}'")
   
   // this property set when running Eclipse headlessly
   j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
@@ -196,7 +198,8 @@ ext {
   testSourceDir = useClover ? cloverTestInstrDir : testDir
   testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
 
-  getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
+  getdownChannelDir = string("${getdown_website_dir}/${propertiesChannelName}")
+  getdownAppBaseDir = string("${jalviewDir}/${getdownChannelDir}/${JAVA_VERSION}")
   getdownArchiveDir = string("${jalviewDir}/${getdown_archive_dir}")
   getdownFullArchiveDir = null
   getdownTextLines = []
@@ -208,7 +211,7 @@ ext {
   // the following values might be overridden by the CHANNEL switch
   getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
   getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
-  getdownArchiveAppBase = null
+  getdownArchiveAppBase = getdown_archive_base
   getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
   getdownAppDistDir = getdown_app_dir_alt
   getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
@@ -227,6 +230,8 @@ ext {
   install4jBackground = string("${install4j_images_dir}/${install4j_background}")
   install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
   install4jCheckSums = true
+
+  applicationName = "${jalview_name}"
   switch (CHANNEL) {
 
     case "BUILD":
@@ -248,7 +253,6 @@ ext {
     reportRsyncCommand = true
     install4jSuffix = ""
     install4jInstallerName = "${jalview_name} Installer"
-    getdownArchiveAppBase = getdown_archive_base
     break
 
     case "ARCHIVE":
@@ -269,7 +273,7 @@ ext {
     case "ARCHIVELOCAL":
     getdownChannelName = string("archive/${JALVIEW_VERSION}")
     getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
-    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+    getdownAppBase = file(getdownAppBaseDir).toURI().toString()
     if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
       throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
     } else {
@@ -333,7 +337,7 @@ ext {
 
     case [ "LOCAL", "JALVIEWJS" ]:
     JALVIEW_VERSION = "TEST"
-    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+    getdownAppBase = file(getdownAppBaseDir).toURI().toString()
     getdownArchiveAppBase = file("${jalviewDir}/${getdown_archive_dir}").toURI().toString()
     getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
     install4jExtraScheme = "jalviewl"
@@ -346,11 +350,13 @@ ext {
 
   }
   JALVIEW_VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+  hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_data_installers_dir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
+  hugoArchiveMdFile = file("${jalviewDir}/${hugo_build_dir}/${hugo_version_archive_dir}/Version-${JALVIEW_VERSION_UNDERSCORES}/_index.md")
   // override getdownAppBase if requested
   if (findProperty("getdown_appbase_override") != null) {
     // revert to LOCAL if empty string
     if (string(getdown_appbase_override) == "") {
-      getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+      getdownAppBase = file(getdownAppBaseDir).toURI().toString()
       getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
     } else if (string(getdown_appbase_override).startsWith("file://")) {
       getdownAppBase = string(getdown_appbase_override)
@@ -364,11 +370,10 @@ ext {
   jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
   // install4j application and folder names
   if (install4jSuffix == "") {
-    install4jApplicationName = "${jalview_name}"
     install4jBundleId = "${install4j_bundle_id}"
     install4jWinApplicationId = install4j_release_win_application_id
   } else {
-    install4jApplicationName = "${jalview_name} ${install4jSuffix}"
+    applicationName = "${jalview_name} ${install4jSuffix}"
     install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
     // add int hash of install4jSuffix to the last part of the application_id
     def id = install4j_release_win_application_id
@@ -378,15 +383,15 @@ ext {
   }
   // sanitise folder and id names
   // install4jApplicationFolder = e.g. "Jalview Build"
-  install4jApplicationFolder = install4jApplicationName
+  install4jApplicationFolder = applicationName
                                     .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
                                     .replaceAll("_+", "_") // collapse __
-  install4jInternalId = install4jApplicationName
+  install4jInternalId = applicationName
                                     .replaceAll(" ","_")
                                     .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
                                     .replaceAll("_+", "") // collapse __
                                     //.replaceAll("_*-_*", "-") // collapse _-_
-  install4jUnixApplicationFolder = install4jApplicationName
+  install4jUnixApplicationFolder = applicationName
                                     .replaceAll(" ","_")
                                     .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
                                     .replaceAll("_+", "_") // collapse __
@@ -394,10 +399,10 @@ ext {
                                     .toLowerCase()
 
   getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
-  getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
-  //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
-  getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
-  getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
+  getdownAppDir = string("${getdownAppBaseDir}/${getdownAppDistDir}")
+  //getdownJ11libDir = "${getdownAppBaseDir}/${getdown_j11lib_dir}"
+  getdownResourceDir = string("${getdownAppBaseDir}/${getdown_resource_dir}")
+  getdownInstallDir = string("${getdownAppBaseDir}/${getdown_install_dir}")
   getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
   getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
   /* compile without modules -- using classpath libraries
@@ -1195,6 +1200,214 @@ task convertMdFiles {
 }
 
 
+def hugoTemplateSubstitutions(String input, Map extras=null) {
+  def replacements = [
+    DATE: getDate("yyyy-MM-dd"),
+    CHANNEL: propertiesChannelName,
+    APPLICATION_NAME: applicationName,
+    GIT_HASH: gitHash,
+    GIT_BRANCH: gitBranch,
+    VERSION: JALVIEW_VERSION,
+    JAVA_VERSION: JAVA_VERSION,
+    VERSION_UNDERSCORES: JALVIEW_VERSION_UNDERSCORES,
+    DRAFT: "false",
+    JVL_HEADER: ""
+  ]
+  def output = input
+  if (extras != null) {
+    extras.each{ k, v ->
+      output = output.replaceAll("__${k}__", ((v == null)?"":v))
+    }
+  }
+  replacements.each{ k, v ->
+    output = output.replaceAll("__${k}__", ((v == null)?"":v))
+  }
+  return output
+}
+
+def mdFileComponents(File mdFile, def dateOnly=false) {
+  def map = [:]
+  def content = ""
+  if (mdFile.exists()) {
+    def inFrontMatter = false
+    def firstLine = true
+    mdFile.eachLine { line ->
+      if (line.matches("---")) {
+        def prev = inFrontMatter
+        inFrontMatter = firstLine
+        if (inFrontMatter != prev)
+          return false
+      }
+      if (inFrontMatter) {
+        def m = null
+        if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) {
+          map["date"] = new Date().parse("yyyy-MM-dd HH:mm:ss", m[0][1])
+        } else if (m = line =~ /^date:\s*(\d{4}-\d{2}-\d{2})/) {
+          map["date"] = new Date().parse("yyyy-MM-dd", m[0][1])
+        } else if (m = line =~ /^channel:\s*(\S+)/) {
+          map["channel"] = m[0][1]
+        } else if (m = line =~ /^version:\s*(\S+)/) {
+          map["version"] = m[0][1]
+        } else if (m = line =~ /^\s*([^:]+)\s*:\s*(\S.*)/) {
+          map[ m[0][1] ] = m[0][2]
+        }
+        if (dateOnly && map["date"] != null) {
+          return false
+        }
+      } else {
+        if (dateOnly)
+          return false
+        content += line+"\n"
+      }
+      firstLine = false
+    }
+  }
+  return dateOnly ? map["date"] : [map, content]
+}
+
+task hugoTemplates {
+  group "website"
+  description "Create partially populated md pages for hugo website build"
+
+  def hugoTemplatesDir = file("${jalviewDir}/${hugo_templates_dir}")
+  def hugoBuildDir = "${jalviewDir}/${hugo_build_dir}"
+  def templateFiles = fileTree(dir: hugoTemplatesDir)
+  def releaseMdFile = file("${jalviewDir}/${releases_dir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
+  def whatsnewMdFile = file("${jalviewDir}/${whatsnew_dir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
+  def oldJvlFile = file("${jalviewDir}/${hugo_old_jvl}")
+  def jalviewjsFile = file("${jalviewDir}/${hugo_jalviewjs}")
+
+  doFirst {
+    // specific release template for version archive
+    def changes = ""
+    def whatsnew = null
+    def givenDate = null
+    def givenChannel = null
+    def givenVersion = null
+    if (CHANNEL == "RELEASE") {
+      def (map, content) = mdFileComponents(releaseMdFile)
+      givenDate = map.date
+      givenChannel = map.channel
+      givenVersion = map.version
+      changes = content
+      if (givenVersion != null && givenVersion != JALVIEW_VERSION) {
+        throw new GradleException("'version' header (${givenVersion}) found in ${releaseMdFile} does not match JALVIEW_VERSION (${JALVIEW_VERSION})")
+      }
+
+      if (whatsnewMdFile.exists())
+        whatsnew = whatsnewMdFile.text
+    }
+
+    def oldJvl = oldJvlFile.exists() ? oldJvlFile.collect{it} : []
+    def jalviewjsLink = jalviewjsFile.exists() ? jalviewjsFile.collect{it} : []
+
+    def changesHugo = null
+    if (changes != null) {
+      changesHugo = '<div class="release_notes">\n\n'
+      def inSection = false
+      changes.eachLine { line ->
+        def m = null
+        if (m = line =~ /^##([^#].*)$/) {
+          if (inSection) {
+            changesHugo += "</div>\n\n"
+          }
+          def section = m[0][1].trim()
+          section = section.toLowerCase()
+          section = section.replaceAll(/ +/, "_")
+          section = section.replaceAll(/[^a-z0-9_\-]/, "")
+          changesHugo += "<div class=\"${section}\">\n\n"
+          inSection = true
+        } else if (m = line =~ /^(\s*-\s*)<!--([^>]+)-->(.*?)(<br\/?>)?\s*$/) {
+          def comment = m[0][2].trim()
+          if (comment != "") {
+            comment = comment.replaceAll('"', "&quot;")
+            def issuekeys = []
+            comment.eachMatch(/JAL-\d+/) { jal -> issuekeys += jal }
+            def newline = m[0][1]
+            if (comment.trim() != "")
+              newline += "{{<comment>}}${comment}{{</comment>}}  "
+            newline += m[0][3].trim()
+            if (issuekeys.size() > 0)
+              newline += "  {{< jal issue=\"${issuekeys.join(",")}\" alt=\"${comment}\" >}}"
+            if (m[0][4] != null)
+              newline += m[0][4]
+            line = newline
+          }
+        }
+        changesHugo += line+"\n"
+      }
+      if (inSection) {
+        changesHugo += "\n</div>\n\n"
+      }
+      changesHugo += '</div>'
+    }
+
+    templateFiles.each{ templateFile ->
+      def newFileName = string(hugoTemplateSubstitutions(templateFile.getName()))
+      def relPath = hugoTemplatesDir.toPath().relativize(templateFile.toPath()).getParent()
+      def newRelPathName = hugoTemplateSubstitutions( relPath.toString() )
+
+      def outPathName = string("${hugoBuildDir}/$newRelPathName")
+
+      copy {
+        from templateFile
+        rename(templateFile.getName(), newFileName)
+        into outPathName
+      }
+
+      def newFile = file("${outPathName}/${newFileName}".toString())
+      def content = newFile.text
+      newFile.text = hugoTemplateSubstitutions(content,
+        [
+          WHATSNEW: whatsnew,
+          CHANGES: changesHugo,
+          DATE: givenDate == null ? "" : givenDate.format("yyyy-MM-dd"),
+          DRAFT: givenDate == null ? "true" : "false",
+          JALVIEWJSLINK: jalviewjsLink.contains(JALVIEW_VERSION) ? "true" : "false",
+          JVL_HEADER: oldJvl.contains(JALVIEW_VERSION) ? "jvl: true" : ""
+        ]
+      )
+    }
+
+  }
+
+  inputs.file(oldJvlFile)
+  inputs.dir(hugoTemplatesDir)
+  inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
+  inputs.property("CHANNEL", { CHANNEL })
+}
+
+def getMdDate(File mdFile) {
+  return mdFileComponents(mdFile, true)
+}
+
+def getMdSections(String content) {
+  def sections = [:]
+  def sectionContent = ""
+  def sectionName = null
+  content.eachLine { line ->
+    def m = null
+    if (m = line =~ /^##([^#].*)$/) {
+      if (sectionName != null) {
+        sections[sectionName] = sectionContent
+        sectionName = null
+        sectionContent = ""
+      }
+      sectionName = m[0][1].trim()
+      sectionName = sectionName.toLowerCase()
+      sectionName = sectionName.replaceAll(/ +/, "_")
+      sectionName = sectionName.replaceAll(/[^a-z0-9_\-]/, "")
+    } else if (sectionName != null) {
+      sectionContent += line+"\n"
+    }
+  }
+  if (sectionContent != null) {
+    sections[sectionName] = sectionContent
+  }
+  return sections
+}
+
+
 task copyHelp(type: Copy) {
   def inputDir = helpSourceDir
   def outputDir = "${helpBuildDir}/${help_dir}"
@@ -1230,6 +1443,142 @@ task copyHelp(type: Copy) {
 }
 
 
+task releasesTemplates {
+  group "help"
+  description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
+
+  dependsOn copyHelp
+
+  def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
+  def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
+  def releasesHtmlFile = file("${helpBuildDir}/${help_dir}/${releases_html}")
+  def whatsnewHtmlFile = file("${helpBuildDir}/${help_dir}/${whatsnew_html}")
+  def releasesMdDir = "${jalviewDir}/${releases_dir}"
+  def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
+
+  doFirst {
+    def releaseMdFile = file("${releasesMdDir}/release-${JALVIEW_VERSION_UNDERSCORES}.md")
+    def whatsnewMdFile = file("${whatsnewMdDir}/whatsnew-${JALVIEW_VERSION_UNDERSCORES}.md")
+
+    if (CHANNEL == "RELEASE") {
+      if (!releaseMdFile.exists()) {
+        throw new GradleException("File ${releaseMdFile} must be created for RELEASE")
+      }
+      if (!whatsnewMdFile.exists()) {
+        throw new GradleException("File ${whatsnewMdFile} must be created for RELEASE")
+      }
+    }
+
+    def releaseFiles = fileTree(dir: releasesMdDir, include: "release-*.md")
+    def releaseFilesDates = releaseFiles.collectEntries {
+      [(it): getMdDate(it)]
+    }
+    releaseFiles = releaseFiles.sort { a,b -> releaseFilesDates[a].compareTo(releaseFilesDates[b]) }
+
+    def releasesTemplate = releasesTemplateFile.text
+    def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
+    def versionTemplate = m[0][1]
+
+    MutableDataSet options = new MutableDataSet()
+
+    def extensions = new ArrayList<>()
+    options.set(Parser.EXTENSIONS, extensions)
+    options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
+
+    Parser parser = Parser.builder(options).build()
+    HtmlRenderer renderer = HtmlRenderer.builder(options).build()
+
+    def actualVersions = releaseFiles.collect { rf ->
+      def (rfMap, rfContent) = mdFileComponents(rf)
+      return rfMap.version
+    }
+    def versionsHtml = ""
+    def linkedVersions = []
+    releaseFiles.reverse().each { rFile ->
+      def (rMap, rContent) = mdFileComponents(rFile)
+
+      def versionLink = ""
+      def partialVersion = ""
+      def firstPart = true
+      rMap.version.split("\\.").each { part ->
+        def displayPart = ( firstPart ? "" : "." ) + part
+        partialVersion += displayPart
+        if (
+            linkedVersions.contains(partialVersion)
+            || ( actualVersions.contains(partialVersion) && partialVersion != rMap.version )
+            ) {
+          versionLink += displayPart
+        } else {
+          versionLink += "<a id=\"Jalview.${partialVersion}\">${displayPart}</a>"
+          linkedVersions += partialVersion
+        }
+        firstPart = false
+      }
+      def displayDate = releaseFilesDates[rFile].format("dd/MM/yyyy")
+
+      def lm = null
+      def rContentProcessed = ""
+      rContent.eachLine { line ->
+        if (lm = line =~ /^(\s*-)(\s*<!--[^>]*?-->)(.*)$/) {
+          line = "${lm[0][1]}${lm[0][3]}${lm[0][2]}"
+      } else if (lm = line =~ /^###([^#]+.*)$/) {
+          line = "_${lm[0][1].trim()}_"
+        }
+        rContentProcessed += line + "\n"
+      }
+
+      def rContentSections = getMdSections(rContentProcessed)
+      def rVersion = versionTemplate
+      if (rVersion != "") {
+        def rNewFeatures = rContentSections["new_features"]
+        def rIssuesResolved = rContentSections["issues_resolved"]
+        Node newFeaturesNode = parser.parse(rNewFeatures)
+        String newFeaturesHtml = renderer.render(newFeaturesNode)
+        Node issuesResolvedNode = parser.parse(rIssuesResolved)
+        String issuesResolvedHtml = renderer.render(issuesResolvedNode)
+        rVersion = hugoTemplateSubstitutions(rVersion,
+          [
+            VERSION: rMap.version,
+            VERSION_LINK: versionLink,
+            DISPLAY_DATE: displayDate,
+            NEW_FEATURES: newFeaturesHtml,
+            ISSUES_RESOLVED: issuesResolvedHtml
+          ]
+        )
+        versionsHtml += rVersion
+      }
+    }
+
+    releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
+    releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
+    releasesHtmlFile.text = releasesTemplate
+
+    if (whatsnewMdFile.exists()) {
+      def wnDisplayDate = releaseFilesDates[releaseMdFile] != null ? releaseFilesDates[releaseMdFile].format("dd MMMM yyyy") : ""
+      def whatsnewMd = hugoTemplateSubstitutions(whatsnewMdFile.text)
+      Node whatsnewNode = parser.parse(whatsnewMd)
+      String whatsnewHtml = renderer.render(whatsnewNode)
+      whatsnewHtml = whatsnewTemplateFile.text.replaceAll("__WHATS_NEW__", whatsnewHtml)
+      whatsnewHtmlFile.text = hugoTemplateSubstitutions(whatsnewHtml,
+        [
+          DISPLAY_DATE: wnDisplayDate
+        ]
+      )
+    } else if (gradle.taskGraph.hasTask(":linkCheck")) {
+      whatsnewHtmlFile.text = "Development build " + getDate("yyyy-MM-dd HH:mm:ss")
+    }
+
+  }
+
+  inputs.file(releasesTemplateFile)
+  inputs.file(whatsnewTemplateFile)
+  inputs.dir(releasesMdDir)
+  inputs.dir(whatsnewMdDir)
+  outputs.file(releasesHtmlFile)
+  outputs.file(whatsnewHtmlFile)
+}
+
+
 task copyResources(type: Copy) {
   group = "build"
   description = "Copy (and make text substitutions in) the resources dir to the build area"
@@ -1327,6 +1676,7 @@ task prepare {
   dependsOn buildResources
   dependsOn copyDocs
   dependsOn copyHelp
+  dependsOn releasesTemplates
   dependsOn convertMdFiles
   dependsOn buildIndices
 }
@@ -1399,7 +1749,7 @@ task linkCheck(type: JavaExec) {
   def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
   classpath = files("${jalviewDir}/${utils_dir}")
   main = "HelpLinksChecker"
-  workingDir = jalviewDir
+  workingDir = "${helpBuildDir}"
   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
 
   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
@@ -1435,7 +1785,7 @@ jar {
   manifest {
     attributes "Main-Class": main_class,
     "Permissions": "all-permissions",
-    "Application-Name": install4jApplicationName,
+    "Application-Name": applicationName,
     "Codebase": application_codebase,
     "Implementation-Version": JALVIEW_VERSION
   }
@@ -1508,7 +1858,7 @@ shadowJar {
   }
   manifest {
     attributes "Implementation-Version": JALVIEW_VERSION,
-    "Application-Name": install4jApplicationName
+    "Application-Name": applicationName
   }
 
   duplicatesStrategy "INCLUDE"
@@ -1533,7 +1883,7 @@ task getdownWebsite() {
 
   doFirst {
     // clean the getdown website and files dir before creating getdown folders
-    delete getdownWebsiteDir
+    delete getdownAppBaseDir
     delete getdownFilesDir
 
     copy {
@@ -1545,7 +1895,7 @@ task getdownWebsite() {
 
     copy {
       from channelPropsFile
-      into getdownWebsiteDir
+      into getdownAppBaseDir
     }
     getdownWebsiteResourceFilenames += file(channelPropsFile).getName()
 
@@ -1570,7 +1920,7 @@ task getdownWebsite() {
     }
 
     props.put("getdown_txt_title", jalview_name)
-    props.put("getdown_txt_ui.name", install4jApplicationName)
+    props.put("getdown_txt_ui.name", applicationName)
 
     // start with appbase
     getdownTextLines += "appbase = ${getdownAppBase}"
@@ -1623,7 +1973,7 @@ task getdownWebsite() {
       if (s.exists()) {
         copy {
           from s
-          into "${getdownWebsiteDir}/${getdown_wrapper_script_dir}"
+          into "${getdownAppBaseDir}/${getdown_wrapper_script_dir}"
         }
         getdownTextLines += "resource = ${getdown_wrapper_script_dir}/${script}"
       }
@@ -1676,18 +2026,18 @@ task getdownWebsite() {
       getdownTextLines += "jvmarg = -Dgetdownappbase=${getdownAppBase}"
     }
 
-    def getdownTxt = file("${getdownWebsiteDir}/getdown.txt")
+    def getdownTxt = file("${getdownAppBaseDir}/getdown.txt")
     getdownTxt.write(getdownTextLines.join("\n"))
 
     getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
-    def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
+    def launchJvl = file("${getdownAppBaseDir}/${getdownLaunchJvl}")
     launchJvl.write("appbase=${getdownAppBase}")
 
     // files going into the getdown website dir: getdown-launcher.jar
     copy {
       from getdownLauncher
       rename(file(getdownLauncher).getName(), getdown_launcher_new)
-      into getdownWebsiteDir
+      into getdownAppBaseDir
     }
 
     // files going into the getdown website dir: getdown-launcher(-local).jar
@@ -1696,7 +2046,7 @@ task getdownWebsite() {
       if (file(getdownLauncher).getName() != getdown_launcher) {
         rename(file(getdownLauncher).getName(), getdown_launcher)
       }
-      into getdownWebsiteDir
+      into getdownAppBaseDir
     }
 
     // files going into the getdown website dir: ./install dir and files
@@ -1723,8 +2073,8 @@ task getdownWebsite() {
       from getdownTxt
       from launchJvl
       from getdownLauncher
-      from "${getdownWebsiteDir}/${getdown_build_properties}"
-      from "${getdownWebsiteDir}/${channel_props}"
+      from "${getdownAppBaseDir}/${getdown_build_properties}"
+      from "${getdownAppBaseDir}/${channel_props}"
       if (file(getdownLauncher).getName() != getdown_launcher) {
         rename(file(getdownLauncher).getName(), getdown_launcher)
       }
@@ -1741,7 +2091,7 @@ task getdownWebsite() {
   if (buildDist) {
     inputs.dir("${jalviewDir}/${package_dir}")
   }
-  outputs.dir(getdownWebsiteDir)
+  outputs.dir(getdownAppBaseDir)
   outputs.dir(getdownFilesDir)
 }
 
@@ -1772,9 +2122,9 @@ task getdownDigest(type: JavaExec) {
     classpath = files(getdownLauncher)
   }
   main = "com.threerings.getdown.tools.Digester"
-  args getdownWebsiteDir
-  inputs.dir(getdownWebsiteDir)
-  outputs.file("${getdownWebsiteDir}/digest2.txt")
+  args getdownAppBaseDir
+  inputs.dir(getdownAppBaseDir)
+  outputs.file("${getdownAppBaseDir}/digest2.txt")
 }
 
 
@@ -1784,7 +2134,7 @@ task getdown() {
   dependsOn getdownDigest
   doLast {
     if (reportRsyncCommand) {
-      def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith('/')?'':'/')
+      def fromDir = getdownAppBaseDir + (getdownAppBaseDir.endsWith('/')?'':'/')
       def toDir = "${getdown_rsync_dest}/${getdownDir}" + (getdownDir.endsWith('/')?'':'/')
       println "LIKELY RSYNC COMMAND:"
       println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
@@ -1816,10 +2166,6 @@ task getdownArchiveBuild() {
   def archiveImagesDir = "${jalviewDir}/${channel_properties_dir}/old/images"
 
   doFirst {
-    if (getdownArchiveAppBase == null) {
-      throw new StopExecutionException("Cannot create getdownArchive for CHANNEL=${CHANNEL}")
-    }
-
     // cleanup old "old" dir
     delete getdownArchiveDir
 
@@ -1830,7 +2176,7 @@ task getdownArchiveBuild() {
 
     // the libdir
     copy {
-      from "${getdownWebsiteDir}/${getdownAppDistDir}"
+      from "${getdownAppBaseDir}/${getdownAppDistDir}"
       into "${getdownFullArchiveDir}/${vAltDir}"
     }
 
@@ -1870,9 +2216,9 @@ task getdownArchiveBuild() {
     // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
     copy {
       from getdownLauncher
-      from "${getdownWebsiteDir}/${getdownLaunchJvl}"
-      from "${getdownWebsiteDir}/${getdown_launcher_new}"
-      from "${getdownWebsiteDir}/${channel_props}"
+      from "${getdownAppBaseDir}/${getdownLaunchJvl}"
+      from "${getdownAppBaseDir}/${getdown_launcher_new}"
+      from "${getdownAppBaseDir}/${channel_props}"
       if (file(getdownLauncher).getName() != getdown_launcher) {
         rename(file(getdownLauncher).getName(), getdown_launcher)
       }
@@ -1912,7 +2258,7 @@ tasks.withType(JavaCompile) {
 
 clean {
   doFirst {
-    delete getdownWebsiteDir
+    delete getdownAppBaseDir
     delete getdownFilesDir
     delete getdownArchiveDir
   }
@@ -2018,8 +2364,7 @@ clean {
 task cleanInstallersDataFiles {
   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
-  def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
-  def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${VERSION_UNDERSCORES}.json")
+  def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
   doFirst {
     delete installersOutputTxt
     delete installersSha256
@@ -2050,7 +2395,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
 
   variables = [
     'JALVIEW_NAME': jalview_name,
-    'JALVIEW_APPLICATION_NAME': install4jApplicationName,
+    'JALVIEW_APPLICATION_NAME': applicationName,
     'JALVIEW_DIR': "../..",
     'OSX_KEYSTORE': OSX_KEYSTORE,
     'OSX_APPLEID': OSX_APPLEID,
@@ -2082,7 +2427,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
     'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
     'INSTALLER_NAME': install4jInstallerName,
     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
-    'GETDOWN_WEBSITE_DIR': getdown_website_dir,
+    'GETDOWN_CHANNEL_DIR': getdownChannelDir,
     'GETDOWN_FILES_DIR': getdown_files_dir,
     'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
     'GETDOWN_DIST_DIR': getdownAppDistDir,
@@ -2131,7 +2476,7 @@ task installerFiles(type: com.install4j.gradle.Install4jTask) {
   }
   //verbose=true
 
-  inputs.dir(getdownWebsiteDir)
+  inputs.dir(getdownAppBaseDir)
   inputs.file(install4jConfFile)
   inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
   inputs.dir(macosJavaVMDir)
@@ -2143,72 +2488,62 @@ def getDataHash(File myFile) {
   HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
   return myFile.exists()
   ? [
-      "filename" : myFile.getName(),
+      "file" : myFile.getName(),
       "filesize" : myFile.length(),
       "sha256" : hash.toString()
     ]
   : null
 }
 
-def writeDataJsonFile(File installersOutputTxt, File installersSha256, File hugoDataJsonFile) {
-  if (!installersOutputTxt.exists()) {
-    throw new GradleException("Required input file '${installersOutputTxt.getPath()}' doesn't exist.")
-    return null;
-  }
-  if (install4jCheckSums && (!installersSha256)) {
-    throw new GradleException("Required input file '${installersSha256.getPath()}' doesn't exist.")
-    return null;
-  }
+def writeDataJsonFile(File installersOutputTxt, File installersSha256, File dataJsonFile) {
   def hash = [
     "channel" : getdownChannelName,
     "date" : getDate("yyyy-MM-dd HH:mm:ss"),
     "git-commit" : "${gitHash} [${gitBranch}]",
     "version" : JALVIEW_VERSION
   ]
-  def idHash = [:]
-  installersOutputTxt.readLines().each { def line ->
-    if (line.startsWith("#")) {
-      return;
-    }
-    line.replaceAll("\n","")
-    def vals = line.split("\t")
-    def filename = vals[3]
-    def filesize = file(filename).length()
-    filename = filename.replaceAll(/^.*\//, "")
-    hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
-    idHash."${filename}" = vals[0]
-  }
-  if (install4jCheckSums) {
-    installersSha256.readLines().each { def line ->
+  // install4j installer files
+  if (installersOutputTxt.exists()) {
+    def idHash = [:]
+    installersOutputTxt.readLines().each { def line ->
       if (line.startsWith("#")) {
         return;
       }
       line.replaceAll("\n","")
-      def vals = line.split(/\s+\*?/)
-      def filename = vals[1]
-      def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
+      def vals = line.split("\t")
+      def filename = vals[3]
+      def filesize = file(filename).length()
+      filename = filename.replaceAll(/^.*\//, "")
+      hash[vals[0]] = [ "id" : vals[0], "os" : vals[1], "name" : vals[2], "file" : filename, "filesize" : filesize ]
+      idHash."${filename}" = vals[0]
+    }
+    if (install4jCheckSums && installersSha256.exists()) {
+      installersSha256.readLines().each { def line ->
+        if (line.startsWith("#")) {
+          return;
+        }
+        line.replaceAll("\n","")
+        def vals = line.split(/\s+\*?/)
+        def filename = vals[1]
+        def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
+      }
     }
   }
 
-  // executable JAR
-  def jarHash = getDataHash(file(shadowJar.archiveFile))
-  if (jarHash != null) {
-    hash."JAR" = jarHash;
-  }
-
-  // version JVL
-  def jvlHash =getDataHash(file(getdownVersionLaunchJvl))
-  if (jvlHash != null) {
-    hash."JVL" = jvlHash;
-  }
-
-  // source TGZ
-  def tarHash = getDataHash(file(sourceDist.archiveFile))
-  if (tarHash != null) {
-    hash."SOURCE" = tarHash;
+  [
+    "JAR": shadowJar.archiveFile, // executable JAR
+    "JVL": getdownVersionLaunchJvl, // version JVL
+    "SOURCE": sourceDist.archiveFile // source TGZ
+  ].each { key, value ->
+    def file = file(value)
+    if (file.exists()) {
+      def fileHash = getDataHash(file)
+      if (fileHash != null) {
+        hash."${key}" = fileHash;
+      }
+    }
   }
-
-  return hugoDataJsonFile.write(new JsonBuilder(hash).toPrettyString())
+  return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
 }
 
 task staticMakeInstallersJsonFile {
@@ -2333,7 +2668,9 @@ task sourceDist(type: Tar) {
     exclude ("utils/InstallAnywhere")
 
     exclude (getdown_files_dir)
-    exclude (getdown_website_dir)
+    // getdown_website_dir and getdown_archive_dir moved to build/website/docroot/getdown
+    //exclude (getdown_website_dir)
+    //exclude (getdown_archive_dir)
 
     // exluding these as not using jars as modules yet
     exclude ("${j11modDir}/**/*.jar")
@@ -2362,21 +2699,34 @@ task sourceDist(type: Tar) {
   }
 }
 
-task makeDataJsonFile {
-  dependsOn installerFiles
-  dependsOn sourceDist
-  dependsOn getdownArchive
-  dependsOn shadowJar
+task dataInstallersJson {
+  group "website"
+  description "Create the installers-VERSION.json data file for installer files created"
+
+  mustRunAfter installers
+  mustRunAfter shadowJar
+  mustRunAfter sourceDist
+  mustRunAfter getdownArchive
 
   def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
   def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
-  def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
-  def hugoDataJsonFile = file("${jalviewDir}/${hugo_build_dir}/data/installers-${VERSION_UNDERSCORES}.json")
 
-  inputs.file(installersOutputTxt)
-  if (install4jCheckSums) {
+  if (installersOutputTxt.exists()) {
+    inputs.file(installersOutputTxt)
+  }
+  if (install4jCheckSums && installersSha256.exists()) {
     inputs.file(installersSha256)
   }
+  [
+    shadowJar.archiveFile, // executable JAR
+    getdownVersionLaunchJvl, // version JVL
+    sourceDist.archiveFile // source TGZ
+  ].each { fileName ->
+    if (file(fileName).exists()) {
+      inputs.file(fileName)
+    }
+  }
+
   outputs.file(hugoDataJsonFile)
 
   doFirst {
@@ -2385,6 +2735,9 @@ task makeDataJsonFile {
 }
 
 task helppages {
+  group "help"
+  description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
+
   dependsOn copyHelp
   dependsOn pubhtmlhelp