--- /dev/null
+#!/usr/bin/env bash
+
+usage() {
+ echo "Usage: $( basename $0 ) [-h] [ [-d] [-m arch] [-c channel] | [-i filename] ] [-a folder] [-u] [-U] [-S] [-P] [-R]"
+ echo ""
+ echo " This script downloads and installs Jalview in macOS, or installs from a previously downloaded Jalview DMG file."
+ echo " One of -d or -i should be specified, whichever comes last is used."
+ echo " Default behaviour with -d is to download the latest release version of Jalview for your architecture and install"
+ echo " the .app in /Applications with user-space updates enabled."
+ echo " These can be adjusted with the following options."
+ echo ""
+ echo " -h Show help"
+ echo " -d Download the latest DMG for the channel"
+ echo " -m arch (with -d) Download specific JVM architecture. Should be one of 'arm64' or 'x86_64' (defaults to \`uname -m\`)"
+ echo " -c channel (with -d) Download from channel. Should be one of 'release', 'test-release', 'develop' (defaults to 'release')."
+ echo " -i filename Use filename as DMG image file instead of downloading."
+ echo " -a folder Install the application .app bundle in folder (default '/Applications')."
+ echo ""
+ echo " Advanced options:"
+ echo ""
+ echo " -u template Use template for user-space updates path (defaults to '~/Library/Application Support/Jalview-Desktop')."
+ echo " template should contain one of: a leading '~/', '%u' (user's username) or '%h' (user's home path)."
+ echo " -U Disable user-space updates (updates will be attempted in the installation folder)."
+ echo " -S Disable all user-space and installation updates."
+ echo " -P Do not add the jalview command-line path to /etc/paths.d."
+ echo " -R Do not perform the check for root privileges."
+ echo " -t tmpdir Use tmpdir for installation files (defaults to /tmp)"
+}
+
+# check OS
+UOS="$(uname -s)"
+if [ "$UOS" != "Darwin" ]; then
+ echo "This install script is for macOS."
+ echo "Jalview can be installed on other OSes using the installers found at"
+ echo "https://www.jalview.org/download/"
+ exit 1
+fi
+
+# set defaults
+DOWNLOAD=0
+UARCH=$(uname -m)
+CHANNEL="release"
+DMGFILE=""
+INSTALLERNAME="$(basename $0)"
+APPLICATIONFOLDER="/Applications"
+USERAPPDIRTEMPLATE=""
+DISABLEUSERAPPDIR=0
+DISABLEALLUPDATES=0
+DISABLEGLOBALPATH=0
+DISABLEROOTCHECK=0
+USETEMPDIR=""
+TMP="/tmp"
+
+# set options
+while getopts "hdi:a:u:c:USPR" opt; do
+ case ${opt} in
+ h)
+ usage
+ exit 0
+ ;;
+ d)
+ DOWNLOAD=1
+ ;;
+ m)
+ UARCH="${OPTARG}"
+ ;;
+ c)
+ CHANNEL="${OPTARG}"
+ ;;
+ i)
+ DMGFILE="${OPTARG}"
+ DOWNLOAD=0
+ ;;
+ a)
+ APPLICATIONFOLDER="${OPTARG}"
+ ;;
+ u)
+ USERAPPDIRTEMPLATE="${OPTARG}"
+ ;;
+ U)
+ DISABLEUSERAPPDIR=1
+ ;;
+ S)
+ DISABLEALLUPDATES=1
+ ;;
+ P)
+ DISABLEGLOBALPATH=1
+ ;;
+ R)
+ DISABLEROOTCHECK=1
+ ;;
+ t)
+ TMP="${OPTARG}"
+ ;;
+ *)
+ echo "Unrecognised option. Run with -h for help."
+ exit 2
+ ;;
+ esac
+done
+
+# no -d or -i arguments
+if [ "${DOWNLOAD}" = 0 -a -z "${DMGFILE}" ]; then
+ usage
+ exit 0
+fi
+
+# root permissions check
+if [ "${DISABLEROOTCHECK}" != 1 -a "${EUID}" != 0 ]; then
+ echo "This script should be run with root permissions, or with -R."
+ exit 5
+fi
+
+# check channel
+case "$CHANNEL" in
+ release|test-release|develop)
+ echo "Using channel ${CHANNEL}"
+ ;;
+ *)
+ echo "-c channel must be one of 'release', 'test-release' or 'develop'"
+ exit 3
+ ;;
+esac
+
+# convert uarch to jarch
+case $UARCH in
+ x86_64)
+ JARCH=x64
+ ;;
+ arm64)
+ JARCH=aarch64
+ ;;
+ *)
+ echo "Unknown architecture '$UARCH'. Exiting."
+ exit 4;
+ ;;
+esac
+
+# dir for downloads and volume mount
+TEMPDIR=$(mktemp -d -p "${TMP}" -t "${INSTALLERNAME%.sh}_${CHANNEL}")
+echo "Using directory '${TEMPDIR}' to download/mount disk image"
+FILEBASE="${TEMPDIR}/jalview-${CHANNEL}-latest-macos-${JARCH}"
+VOLUMEDIR="${FILEBASE}.vol"
+
+if [ "${DOWNLOAD}" = 1 ]; then
+ DMGFILE="${FILEBASE}.dmg"
+ SHA256=""
+
+ URL="https://www.jalview.org/downloads/${CHANNEL}/installer/macos-${JARCH}"
+
+ echo "Downloading '${URL}' to '${DMGFILE}'"
+ curl -f -L -o "${DMGFILE}" "${URL}"
+ [ $? != 0 ] && echo "Could not download '$URL' to '$DMGFILE'" && exit 6
+ SHA256=$( curl -f -s -L "${URL}.sha256" )
+ [ $? != 0 ] && echo "Could not download '$URL.sha256'" && exit 7
+
+ CHECK=$( shasum -a 256 "${DMGFILE}" | cut -d" " -f1 )
+ if [ "${CHECK}" = "${SHA256}" ]; then
+ echo "Downloaded file '$DMGFILE' checksum matches downloaded checksum '$SHA256'"
+ else
+ echo "Downloaded file '$DMGFILE' checksum does not match downloaded checksum '$SHA256'"
+ exit 8
+ fi
+fi
+
+# mount the DMG image
+hdiutil attach -mountpoint "${VOLUMEDIR}" "${DMGFILE}"
+[ $? != 0 ] && echo "Could not mount '${DMGFILE}' on mount point '${VOLUMEDIR}'" && exit 9
+
+# difficult to use a wildcard for a non-user-readable folder without inserting sudo directly into the command
+# i.e. what I really want to do here is
+# APP=$( sudo sh -c "ls -1d ${VOLUMEDIR}/Jalview*.app" | head -1 )
+# but don't want to put sudo in the script because of the -R option. We'll loop through instead.
+VOLUMEFILES=$( ls -1 ${VOLUMEDIR} )
+while IFS= read -r VOLUMEFILE; do
+ FILE=$( basename "${VOLUMEFILE}" )
+ if [ "${FILE}" != "${FILE#Jalview}" -a "${FILE}" != "${FILE%.app}" ]; then
+ APP="${FILE}"
+ break
+ fi
+done <<< "$VOLUMEFILES"
+[ -z "${APP}" ] && echo "Could not find Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 10
+
+APPNAME=$( basename "$APP" )
+
+# ensure no trailing slash on APPNAME or APPLICATIONFOLDER (important for rsync)
+while [ "${APPLICATIONFOLDER}" != "${APPLICATIONFOLDER%/}" -a "${APPLICATIONFOLDER}" != "/" ]; do
+ APPLICATIONFOLDER=${APPLICATIONFOLDER%/}
+done
+while [ "${APPNAME}" != "${APPNAME%/}" ]; do
+ APPNAME=${APPNAME%/}
+done
+[ -z "${APPNAME}" ] && echo "Could not find suitable Jalview\*.app in the volume '${VOLUMEDIR}'" && exit 11
+
+# rsync .app from mounted volume to application folder
+rsync -avh --delete "${VOLUMEDIR}/${APPNAME}" "${APPLICATIONFOLDER}/"
+[ $? != 0 ] && echo "Possible problem when rsyncing '${APP}' to '${APPLICATIONFOLDER}'" && exit 12
+
+EXIT=0
+declare -a WARNINGS=()
+addwarning() {
+ W=$1
+ N=$2
+ echo "$W"
+ WARNINGS=( "${WARNINGS[@]}" "$W" )
+ [ "${EXIT}" = 0 ] && EXIT=$N
+}
+
+# unmount the DMG image
+hdiutil detach "${VOLUMEDIR}" || addwarning "Possible problem when unmounting/deleting '${VOLUMEDIR}'. I'm continuing the install but you should look at it later." 13
+
+# delete the image file and temp dir
+if [ "${DOWNLOAD}" = 1 ]; then
+ rm "${DMGFILE}"
+fi
+rmdir "${TEMPDIR}"
+
+VMOPTIONS="${APPLICATIONFOLDER}/${APPNAME}/Contents/vmoptions.txt"
+VMOPTIONS_COMMENT=$( cat << EOM
+#
+# Jalview specific options below
+EOM
+)
+
+# -Dnouserdefaultappdir=false
+if [ "${DISABLEUSERAPPDIR}" = 1 ]; then
+ VMOPTIONS_USERAPPDIR=$( cat << EOM
+#
+# Comment out the following line to allow user-space updates
+-Dnouserdefaultappdir=true
+EOM
+)
+else
+ VMOPTIONS_USERAPPDIR=$( cat << EOM
+#
+# Uncomment the following line to disable user-space updates
+#-Dnouserdefaultappdir=true
+EOM
+)
+fi
+
+# -Dsetuserappdirpath
+if [ -z "${USERAPPDIRTEMPLATE}" ]; then
+ VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
+#
+# Uncomment the below line to set a custom path for user-space updates -- use with caution.
+# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
+# If not set, the default is ~/Library/Application Support/Jalview-Desktop for macOS
+#-Dsetuserappdirpath=/tmp/jalview/%u
+EOM
+)
+else
+ VMOPTIONS_USERAPPDIRPATH=$( cat << EOM
+#
+# The below line sets a custom path for user-space updates -- use with caution.
+# A leading ~/ or %h anywhere will be substituted with the user's home path, and %u by the username.
+# If unset, the default is ~/Library/Application Support/Jalview-Desktop for macOS
+-Dsetuserappdirpath=${USERAPPDIRTEMPLATE}
+EOM
+)
+fi
+
+# -Dsilent=noupdate
+if [ "${DISABLEALLUPDATES}" = 1 ]; then
+ VMOPTIONS_UPDATES=$( cat << EOM
+#
+# Comment out the following line to enable updates
+-Dsilent=noupdate
+EOM
+)
+else
+ VMOPTIONS_UPDATES=$( cat << EOM
+#
+# Uncomment the following line to also disable all updates
+#-Dsilent=noupdate
+EOM
+)
+fi
+
+printf "%s\n%s\n%s\n%s\n" "${VMOPTIONS_COMMENT}" "${VMOPTIONS_USERAPPDIR}" "${VMOPTIONS_USERAPPDIRPATH}" "${VMOPTIONS_UPDATES}" >> "${VMOPTIONS}" || addwarning "Possible problem adding options to '${VMOPTIONS}'" 14
+
+# set a global path in /etc/paths.d
+if [ "${DISABLEGLOBALPATH}" != 1 ]; then
+ LONGHASH=$( printf %s "${APPLICATIONFOLDER}/${APPNAME}/Contents/Resources/app" | shasum -a 256 -b )
+ HASH=${LONGHASH:0:8}
+ NAME=${APPNAME%.app}
+ US_NAME=${APPNAME// /_}
+ PATHFILE="/etc/paths.d/${US_NAME}-${HASH}"
+ echo "${APPLICATIONFOLDER}/${APPNAME}/Contents/MacOS" > "${PATHFILE}" || addwarning "Possible problem writing path file '${PATHFILE}'" 15
+fi
+
+# show accumulated warnings
+if [ "${#WARNINGS[@]}" != 0 ]; then
+ echo "-----------------"
+ echo "Installation complete."
+ echo "Warnings summary:"
+ printf -- '- %s\n' "${WARNINGS[@]}"
+else
+ echo "Successful installation!"
+fi
+
+exit $EXIT