JAL-4004 gradle task releasesTemplates now part of main build dependency tree. Improv...
authorBen Soares <bsoares@dundee.ac.uk>
Mon, 9 May 2022 22:21:27 +0000 (23:21 +0100)
committerBen Soares <bsoares@dundee.ac.uk>
Mon, 9 May 2022 22:21:27 +0000 (23:21 +0100)
build.gradle

index 958343b..1056124 100644 (file)
@@ -1199,7 +1199,345 @@ 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 releasesTemplates {
+  group "help"
+  description "Recreate whatsNew.html and releases.html from markdown files and templates in help"
+
+  def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
+  def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
+  def releasesHtmlFile = file("${helpSourceDir}/${releases_html}")
+  def whatsnewHtmlFile = file("${helpSourceDir}/${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("d MMMMM 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 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,
+        [:]
+      )
+    }
+
+  }
+
+  inputs.file(releasesTemplateFile)
+  inputs.file(whatsnewTemplateFile)
+  inputs.dir(releasesMdDir)
+  inputs.dir(whatsnewMdDir)
+  outputs.file(releasesHtmlFile)
+  outputs.file(whatsnewHtmlFile)
+}
+
+
 task copyHelp(type: Copy) {
+  dependsOn releasesTemplates
+
   def inputDir = helpSourceDir
   def outputDir = "${helpBuildDir}/${help_dir}"
   from(inputDir) {
@@ -1989,703 +2327,409 @@ task copyInstall4jTemplate {
     } else {
       // remove the examples subdir from Full File Set
       def files = install4jConfigXml.files[0]
-      def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
-      def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
-      def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
-      def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
-      dirEntry.parent().remove(dirEntry)
-    }
-    install4jConfigXml.'**'.action.any { a ->
-      if (a.'@customizedId' == customizedIdToDelete) {
-        def parent = a.parent()
-        parent.remove(a)
-        return true
-      }
-    }
-
-    // write install4j file
-    install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
-  }
-}
-
-
-clean {
-  doFirst {
-    delete install4jConfFile
-  }
-}
-
-task cleanInstallersDataFiles {
-  def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
-  def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
-  def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
-  doFirst {
-    delete installersOutputTxt
-    delete installersSha256
-    delete hugoDataJsonFile
-  }
-}
-
-task installerFiles(type: com.install4j.gradle.Install4jTask) {
-  group = "distribution"
-  description = "Create the install4j installers"
-  dependsOn getdown
-  dependsOn copyInstall4jTemplate
-  dependsOn cleanInstallersDataFiles
-
-  projectFile = install4jConfFile
-
-  // create an md5 for the input files to use as version for install4j conf file
-  def digest = MessageDigest.getInstance("MD5")
-  digest.update(
-    (file("${install4jDir}/${install4j_template}").text + 
-    file("${install4jDir}/${install4j_info_plist_file_associations}").text +
-    file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
-  def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
-  if (filesMd5.length() >= 8) {
-    filesMd5 = filesMd5.substring(0,8)
-  }
-  def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
-
-  variables = [
-    'JALVIEW_NAME': jalview_name,
-    'JALVIEW_APPLICATION_NAME': applicationName,
-    'JALVIEW_DIR': "../..",
-    'OSX_KEYSTORE': OSX_KEYSTORE,
-    'OSX_APPLEID': OSX_APPLEID,
-    'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
-    'JSIGN_SH': JSIGN_SH,
-    'JRE_DIR': getdown_app_dir_java,
-    'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
-    'JALVIEW_VERSION': JALVIEW_VERSION,
-    'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
-    'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
-    'JAVA_VERSION': JAVA_VERSION,
-    'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
-    'VERSION': JALVIEW_VERSION,
-    'MACOS_JAVA_VM_DIR': macosJavaVMDir,
-    'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
-    'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
-    'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
-    'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
-    'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
-    'COPYRIGHT_MESSAGE': install4j_copyright_message,
-    'BUNDLE_ID': install4jBundleId,
-    'INTERNAL_ID': install4jInternalId,
-    'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
-    'MACOS_DMG_DS_STORE': install4jDMGDSStore,
-    'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
-    'WRAPPER_LINK': getdownWrapperLink,
-    'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
-    'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
-    'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
-    'INSTALLER_NAME': install4jInstallerName,
-    'INSTALL4J_UTILS_DIR': install4j_utils_dir,
-    'GETDOWN_CHANNEL_DIR': getdownChannelDir,
-    'GETDOWN_FILES_DIR': getdown_files_dir,
-    'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
-    'GETDOWN_DIST_DIR': getdownAppDistDir,
-    'GETDOWN_ALT_DIR': getdown_app_dir_alt,
-    'GETDOWN_INSTALL_DIR': getdown_install_dir,
-    'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
-    'BUILD_DIR': install4jBuildDir,
-    'APPLICATION_CATEGORIES': install4j_application_categories,
-    'APPLICATION_FOLDER': install4jApplicationFolder,
-    'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
-    'EXECUTABLE_NAME': install4jExecutableName,
-    'EXTRA_SCHEME': install4jExtraScheme,
-    'MAC_ICONS_FILE': install4jMacIconsFile,
-    'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
-    'PNG_ICON_FILE': install4jPngIconFile,
-    'BACKGROUND': install4jBackground,
-
-  ]
-
-  //println("INSTALL4J VARIABLES:")
-  //variables.each{k,v->println("${k}=${v}")}
-
-  destination = "${jalviewDir}/${install4jBuildDir}"
-  buildSelected = true
-
-  if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
-    faster = true
-    disableSigning = true
-    disableNotarization = true
-  }
-
-  if (OSX_KEYPASS) {
-    macKeystorePassword = OSX_KEYPASS
-  } 
-  
-  if (OSX_ALTOOLPASS) {
-    appleIdPassword = OSX_ALTOOLPASS
-    disableNotarization = false
-  } else {
-    disableNotarization = true
-  }
-
-  doFirst {
-    println("Using projectFile "+projectFile)
-    if (!disableNotarization) { println("Will notarize OSX App DMG") }
-  }
-  //verbose=true
-
-  inputs.dir(getdownAppBaseDir)
-  inputs.file(install4jConfFile)
-  inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
-  inputs.dir(macosJavaVMDir)
-  inputs.dir(windowsJavaVMDir)
-  outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
-}
-
-def getDataHash(File myFile) {
-  HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
-  return myFile.exists()
-  ? [
-      "file" : myFile.getName(),
-      "filesize" : myFile.length(),
-      "sha256" : hash.toString()
-    ]
-  : 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
-  ]
-  // install4j installer files
-  if (installersOutputTxt.exists()) {
-    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.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]
-      }
-    }
-  }
-
-  [
-    "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;
+      def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
+      def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
+      def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
+      def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
+      dirEntry.parent().remove(dirEntry)
+    }
+    install4jConfigXml.'**'.action.any { a ->
+      if (a.'@customizedId' == customizedIdToDelete) {
+        def parent = a.parent()
+        parent.remove(a)
+        return true
       }
     }
-  }
-  return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
-}
 
-task staticMakeInstallersJsonFile {
-  doFirst {
-    def output = findProperty("i4j_output")
-    def sha256 = findProperty("i4j_sha256")
-    def json = findProperty("i4j_json")
-    if (output == null || sha256 == null || json == null) {
-      throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
-    }
-    writeDataJsonFile(file(output), file(sha256), file(json))
+    // write install4j file
+    install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
   }
 }
 
-task installers {
-  dependsOn installerFiles
-}
-
 
-spotless {
-  java {
-    eclipse().configFile(eclipse_codestyle_file)
+clean {
+  doFirst {
+    delete install4jConfFile
   }
 }
 
-task createSourceReleaseProperties(type: WriteProperties) {
-  group = "distribution"
-  description = "Create the source RELEASE properties file"
-  
-  def sourceTarBuildDir = "${buildDir}/sourceTar"
-  def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
-  outputFile (sourceReleasePropertiesFile)
-
+task cleanInstallersDataFiles {
+  def installersOutputTxt = file("${jalviewDir}/${install4jBuildDir}/output.txt")
+  def installersSha256 = file("${jalviewDir}/${install4jBuildDir}/sha256sums")
+  def hugoDataJsonFile = file("${jalviewDir}/${install4jBuildDir}/installers-${JALVIEW_VERSION_UNDERSCORES}.json")
   doFirst {
-    releaseProps.each{ key, val -> property key, val }
-    property "git.branch", gitBranch
-    property "git.hash", gitHash
+    delete installersOutputTxt
+    delete installersSha256
+    delete hugoDataJsonFile
   }
-
-  outputs.file(outputFile)
 }
 
-task sourceDist(type: Tar) {
-  group "distribution"
-  description "Create a source .tar.gz file for distribution"
-
-  dependsOn createBuildProperties
-  dependsOn convertMdFiles
-  dependsOn eclipseAllPreferences
-  dependsOn createSourceReleaseProperties
-
-
-  def outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
-  archiveFileName = outputFileName
-  
-  compression Compression.GZIP
-  
-  into project.name
-
-  def EXCLUDE_FILES=[
-    "build/*",
-    "bin/*",
-    "test-output/",
-    "test-reports",
-    "tests",
-    "clover*/*",
-    ".*",
-    "benchmarking/*",
-    "**/.*",
-    "*.class",
-    "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
-    "*locales/**",
-    "utils/InstallAnywhere",
-    "**/*.log",
-    "RELEASE",
-  ] 
-  def PROCESS_FILES=[
-    "AUTHORS",
-    "CITATION",
-    "FEATURETODO",
-    "JAVA-11-README",
-    "FEATURETODO",
-    "LICENSE",
-    "**/README",
-    "THIRDPARTYLIBS",
-    "TESTNG",
-    "build.gradle",
-    "gradle.properties",
-    "**/*.java",
-    "**/*.html",
-    "**/*.xml",
-    "**/*.gradle",
-    "**/*.groovy",
-    "**/*.properties",
-    "**/*.perl",
-    "**/*.sh",
-  ]
-  def INCLUDE_FILES=[
-    ".classpath",
-    ".settings/org.eclipse.buildship.core.prefs",
-    ".settings/org.eclipse.jdt.core.prefs"
-  ]
-
-  from(jalviewDir) {
-    exclude (EXCLUDE_FILES)
-    include (PROCESS_FILES)
-    filter(ReplaceTokens,
-      beginToken: '$$',
-      endToken: '$$',
-      tokens: [
-        'Version-Rel': JALVIEW_VERSION,
-        'Year-Rel': getDate("yyyy")
-      ]
-    )
-  }
-  from(jalviewDir) {
-    exclude (EXCLUDE_FILES)
-    exclude (PROCESS_FILES)
-    exclude ("appletlib")
-    exclude ("**/*locales")
-    exclude ("*locales/**")
-    exclude ("utils/InstallAnywhere")
-
-    exclude (getdown_files_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")
-  }
-  from(jalviewDir) {
-    include(INCLUDE_FILES)
-  }
-//  from (jalviewDir) {
-//    // explicit includes for stuff that seemed to not get included
-//    include(fileTree("test/**/*."))
-//    exclude(EXCLUDE_FILES)
-//    exclude(PROCESS_FILES)
-//  }
+task installerFiles(type: com.install4j.gradle.Install4jTask) {
+  group = "distribution"
+  description = "Create the install4j installers"
+  dependsOn getdown
+  dependsOn copyInstall4jTemplate
+  dependsOn cleanInstallersDataFiles
 
-  from(file(buildProperties).getParent()) {
-    include(file(buildProperties).getName())
-    rename(file(buildProperties).getName(), "build_properties")
-    filter({ line ->
-      line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
-    })
-  }
+  projectFile = install4jConfFile
 
-  def sourceTarBuildDir = "${buildDir}/sourceTar"
-  from(sourceTarBuildDir) {
-    // this includes the appended RELEASE properties file
+  // create an md5 for the input files to use as version for install4j conf file
+  def digest = MessageDigest.getInstance("MD5")
+  digest.update(
+    (file("${install4jDir}/${install4j_template}").text + 
+    file("${install4jDir}/${install4j_info_plist_file_associations}").text +
+    file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
+  def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
+  if (filesMd5.length() >= 8) {
+    filesMd5 = filesMd5.substring(0,8)
   }
-}
-
-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 install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
 
-  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)
-    }
-  }
+  variables = [
+    'JALVIEW_NAME': jalview_name,
+    'JALVIEW_APPLICATION_NAME': applicationName,
+    'JALVIEW_DIR': "../..",
+    'OSX_KEYSTORE': OSX_KEYSTORE,
+    'OSX_APPLEID': OSX_APPLEID,
+    'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
+    'JSIGN_SH': JSIGN_SH,
+    'JRE_DIR': getdown_app_dir_java,
+    'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
+    'JALVIEW_VERSION': JALVIEW_VERSION,
+    'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
+    'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
+    'JAVA_VERSION': JAVA_VERSION,
+    'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
+    'VERSION': JALVIEW_VERSION,
+    'MACOS_JAVA_VM_DIR': macosJavaVMDir,
+    'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
+    'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
+    'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
+    'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
+    'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
+    'COPYRIGHT_MESSAGE': install4j_copyright_message,
+    'BUNDLE_ID': install4jBundleId,
+    'INTERNAL_ID': install4jInternalId,
+    'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
+    'MACOS_DMG_DS_STORE': install4jDMGDSStore,
+    'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
+    'WRAPPER_LINK': getdownWrapperLink,
+    'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
+    'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+    'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
+    'INSTALLER_NAME': install4jInstallerName,
+    'INSTALL4J_UTILS_DIR': install4j_utils_dir,
+    'GETDOWN_CHANNEL_DIR': getdownChannelDir,
+    'GETDOWN_FILES_DIR': getdown_files_dir,
+    'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
+    'GETDOWN_DIST_DIR': getdownAppDistDir,
+    'GETDOWN_ALT_DIR': getdown_app_dir_alt,
+    'GETDOWN_INSTALL_DIR': getdown_install_dir,
+    'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
+    'BUILD_DIR': install4jBuildDir,
+    'APPLICATION_CATEGORIES': install4j_application_categories,
+    'APPLICATION_FOLDER': install4jApplicationFolder,
+    'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
+    'EXECUTABLE_NAME': install4jExecutableName,
+    'EXTRA_SCHEME': install4jExtraScheme,
+    'MAC_ICONS_FILE': install4jMacIconsFile,
+    'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
+    'PNG_ICON_FILE': install4jPngIconFile,
+    'BACKGROUND': install4jBackground,
 
-  outputs.file(hugoDataJsonFile)
+  ]
 
-  doFirst {
-    writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
-  }
-}
+  //println("INSTALL4J VARIABLES:")
+  //variables.each{k,v->println("${k}=${v}")}
 
-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))
-    }
+  destination = "${jalviewDir}/${install4jBuildDir}"
+  buildSelected = true
+
+  if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
+    faster = true
+    disableSigning = true
+    disableNotarization = true
   }
-  replacements.each{ k, v ->
-    output = output.replaceAll("__${k}__", ((v == null)?"":v))
+
+  if (OSX_KEYPASS) {
+    macKeystorePassword = OSX_KEYPASS
+  } 
+  
+  if (OSX_ALTOOLPASS) {
+    appleIdPassword = OSX_ALTOOLPASS
+    disableNotarization = false
+  } else {
+    disableNotarization = true
   }
-  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
-    }
+  doFirst {
+    println("Using projectFile "+projectFile)
+    if (!disableNotarization) { println("Will notarize OSX App DMG") }
   }
-  return dateOnly ? map["date"] : [map, content]
-}
+  //verbose=true
 
-task hugoTemplates {
-  group "website"
-  description "Create partially populated md pages for hugo website build"
+  inputs.dir(getdownAppBaseDir)
+  inputs.file(install4jConfFile)
+  inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
+  inputs.dir(macosJavaVMDir)
+  inputs.dir(windowsJavaVMDir)
+  outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
+}
 
-  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}")
+def getDataHash(File myFile) {
+  HashCode hash = Files.asByteSource(myFile).hash(Hashing.sha256())
+  return myFile.exists()
+  ? [
+      "file" : myFile.getName(),
+      "filesize" : myFile.length(),
+      "sha256" : hash.toString()
+    ]
+  : null
+}
 
-  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})")
+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
+  ]
+  // install4j installer files
+  if (installersOutputTxt.exists()) {
+    def idHash = [:]
+    installersOutputTxt.readLines().each { def line ->
+      if (line.startsWith("#")) {
+        return;
       }
-
-      if (whatsnewMdFile.exists())
-        whatsnew = whatsnewMdFile.text
+      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]
     }
-
-    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
-          }
+    if (install4jCheckSums && installersSha256.exists()) {
+      installersSha256.readLines().each { def line ->
+        if (line.startsWith("#")) {
+          return;
         }
-        changesHugo += line+"\n"
-      }
-      if (inSection) {
-        changesHugo += "\n</div>\n\n"
+        line.replaceAll("\n","")
+        def vals = line.split(/\s+\*?/)
+        def filename = vals[1]
+        def innerHash = (hash.(idHash."${filename}"))."sha256" = vals[0]
       }
-      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
+  [
+    "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;
       }
-
-      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" : ""
-        ]
-      )
     }
-
   }
+  return dataJsonFile.write(new JsonBuilder(hash).toPrettyString())
+}
 
-  inputs.file(oldJvlFile)
-  inputs.dir(hugoTemplatesDir)
-  inputs.property("JALVIEW_VERSION", { JALVIEW_VERSION })
-  inputs.property("CHANNEL", { CHANNEL })
+task staticMakeInstallersJsonFile {
+  doFirst {
+    def output = findProperty("i4j_output")
+    def sha256 = findProperty("i4j_sha256")
+    def json = findProperty("i4j_json")
+    if (output == null || sha256 == null || json == null) {
+      throw new GradleException("Must provide paths to all of output.txt, sha256sums, and output.json with '-Pi4j_output=... -Pi4j_sha256=... -Pi4j_json=...")
+    }
+    writeDataJsonFile(file(output), file(sha256), file(json))
+  }
 }
 
-def getMdDate(File mdFile) {
-  return mdFileComponents(mdFile, true)
+task installers {
+  dependsOn installerFiles
 }
 
-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
+
+spotless {
+  java {
+    eclipse().configFile(eclipse_codestyle_file)
   }
-  return sections
 }
 
-task releasesTemplates {
-  def releasesTemplateFile = file("${jalviewDir}/${releases_template}")
-  def whatsnewTemplateFile = file("${jalviewDir}/${whatsnew_template}")
-  def releasesHtmlFile = file("${helpSourceDir}/${releases_html}")
-  def whatsnewHtmlFile = file("${helpSourceDir}/${whatsnew_html}")
-  def releasesMdDir = "${jalviewDir}/${releases_dir}"
-  def whatsnewMdDir = "${jalviewDir}/${whatsnew_dir}"
+task createSourceReleaseProperties(type: WriteProperties) {
+  group = "distribution"
+  description = "Create the source RELEASE properties file"
+  
+  def sourceTarBuildDir = "${buildDir}/sourceTar"
+  def sourceReleasePropertiesFile = "${sourceTarBuildDir}/RELEASE"
+  outputFile (sourceReleasePropertiesFile)
 
   doFirst {
-    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]) }
+    releaseProps.each{ key, val -> property key, val }
+    property "git.branch", gitBranch
+    property "git.hash", gitHash
+  }
 
-    def releasesTemplate = releasesTemplateFile.text
-    def m = releasesTemplate =~ /(?s)__VERSION_LOOP_START__(.*)__VERSION_LOOP_END__/
-    def versionTemplate = m[0][1]
+  outputs.file(outputFile)
+}
 
-    MutableDataSet options = new MutableDataSet()
+task sourceDist(type: Tar) {
+  group "distribution"
+  description "Create a source .tar.gz file for distribution"
 
-    def extensions = new ArrayList<>()
-    options.set(Parser.EXTENSIONS, extensions)
-    options.set(Parser.HTML_BLOCK_COMMENT_ONLY_FULL_LINE, true)
+  dependsOn createBuildProperties
+  dependsOn convertMdFiles
+  dependsOn eclipseAllPreferences
+  dependsOn createSourceReleaseProperties
 
-    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 outputFileName = "${project.name}_${JALVIEW_VERSION_UNDERSCORES}.tar.gz"
+  archiveFileName = outputFileName
+  
+  compression Compression.GZIP
+  
+  into project.name
 
-      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("d MMMMM yyyy")
+  def EXCLUDE_FILES=[
+    "build/*",
+    "bin/*",
+    "test-output/",
+    "test-reports",
+    "tests",
+    "clover*/*",
+    ".*",
+    "benchmarking/*",
+    "**/.*",
+    "*.class",
+    "**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales",
+    "*locales/**",
+    "utils/InstallAnywhere",
+    "**/*.log",
+    "RELEASE",
+  ] 
+  def PROCESS_FILES=[
+    "AUTHORS",
+    "CITATION",
+    "FEATURETODO",
+    "JAVA-11-README",
+    "FEATURETODO",
+    "LICENSE",
+    "**/README",
+    "THIRDPARTYLIBS",
+    "TESTNG",
+    "build.gradle",
+    "gradle.properties",
+    "**/*.java",
+    "**/*.html",
+    "**/*.xml",
+    "**/*.gradle",
+    "**/*.groovy",
+    "**/*.properties",
+    "**/*.perl",
+    "**/*.sh",
+  ]
+  def INCLUDE_FILES=[
+    ".classpath",
+    ".settings/org.eclipse.buildship.core.prefs",
+    ".settings/org.eclipse.jdt.core.prefs"
+  ]
 
-      def rContentSections = getMdSections(rContent)
-      def rVersion = versionTemplate
-      if (rVersion != "") {
-        Node newFeaturesNode = parser.parse(rContentSections["new_features"])
-        String newFeaturesHtml = renderer.render(newFeaturesNode)
-        Node issuesResolvedNode = parser.parse(rContentSections["issues_resolved"])
-        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
-      }
-    }
+  from(jalviewDir) {
+    exclude (EXCLUDE_FILES)
+    include (PROCESS_FILES)
+    filter(ReplaceTokens,
+      beginToken: '$$',
+      endToken: '$$',
+      tokens: [
+        'Version-Rel': JALVIEW_VERSION,
+        'Year-Rel': getDate("yyyy")
+      ]
+    )
+  }
+  from(jalviewDir) {
+    exclude (EXCLUDE_FILES)
+    exclude (PROCESS_FILES)
+    exclude ("appletlib")
+    exclude ("**/*locales")
+    exclude ("*locales/**")
+    exclude ("utils/InstallAnywhere")
 
-    releasesTemplate = releasesTemplate.replaceAll("(?s)__VERSION_LOOP_START__.*__VERSION_LOOP_END__", versionsHtml)
-    releasesTemplate = hugoTemplateSubstitutions(releasesTemplate)
-    releasesHtmlFile.text = releasesTemplate
+    exclude (getdown_files_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")
+  }
+  from(jalviewDir) {
+    include(INCLUDE_FILES)
   }
+//  from (jalviewDir) {
+//    // explicit includes for stuff that seemed to not get included
+//    include(fileTree("test/**/*."))
+//    exclude(EXCLUDE_FILES)
+//    exclude(PROCESS_FILES)
+//  }
 
-  inputs.file(releasesTemplateFile)
-  inputs.file(whatsnewTemplateFile)
-  inputs.dir(releasesMdDir)
-  inputs.dir(whatsnewMdDir)
-  outputs.file(releasesHtmlFile)
-  outputs.file(whatsnewHtmlFile)
+  from(file(buildProperties).getParent()) {
+    include(file(buildProperties).getName())
+    rename(file(buildProperties).getName(), "build_properties")
+    filter({ line ->
+      line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
+    })
+  }
+
+  def sourceTarBuildDir = "${buildDir}/sourceTar"
+  from(sourceTarBuildDir) {
+    // this includes the appended RELEASE properties file
+  }
 }
 
+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")
+
+  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 {
+    writeDataJsonFile(installersOutputTxt, installersSha256, hugoDataJsonFile)
+  }
+}
 
 task helppages {
+  group "help"
+  description "Copies all help pages to build dir. Runs ant task 'pubhtmlhelp'."
+
   dependsOn copyHelp
   dependsOn pubhtmlhelp