diff --git a/README.md b/README.md index 52fb564..b00e1ec 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![codecov](https://codecov.io/gh/mbhall88/drprg/branch/main/graph/badge.svg?token=2WAA6MZRKK)](https://codecov.io/gh/mbhall88/drprg) [![Rust](https://github.com/mbhall88/drprg/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/mbhall88/drprg/actions/workflows/rust.yml) +[![github release version](https://img.shields.io/github/v/release/mbhall88/drprg)](https://github.com/mbhall88/drprg/releases) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) Full documentation: diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b3b0806..e2c4438 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -15,3 +15,6 @@ - [VCF to build reference graph](./guide/build/vcf.md) - [Catalogue of mutations](./guide/build/catalogue.md) - [Expert rules](./guide/build/expert.md) + +[Contributing](./contributing.md) +[Changes](../../CHANGELOG.md) \ No newline at end of file diff --git a/docs/src/contributing.md b/docs/src/contributing.md new file mode 100644 index 0000000..3da57ac --- /dev/null +++ b/docs/src/contributing.md @@ -0,0 +1,7 @@ +# Contributing + +If you would like to contribute to Dr. PRG, feel free to open a pull request with +suggested changes, or even raise an issue to discuss potential contributions. + +If you would like to contribute a prebuilt index for a species, head on over +to https://github.com/mbhall88/drprg-index \ No newline at end of file diff --git a/docs/src/installation.md b/docs/src/installation.md index 58ffc85..0f2b108 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -2,6 +2,10 @@ ## Conda +[![Conda (channel only)](https://img.shields.io/conda/vn/bioconda/drprg)](https://anaconda.org/bioconda/drprg) +[![bioconda version](https://anaconda.org/bioconda/drprg/badges/platforms.svg)](https://anaconda.org/bioconda/drprg) +![Conda](https://img.shields.io/conda/dn/bioconda/drprg) + ``` conda install drprg ``` @@ -37,6 +41,59 @@ To run `drprg` using the above container with [Singularity] $ singularity exec "docker://$URI" drprg --help ``` +## Prebuilt binary + +If you use the prebuilt binary, you must have the [external dependecies](#dependencies) installed separately. + +```shell +curl -sSL drprg.mbh.sh | sh +# or with wget +wget -nv -O - drprg.mbh.sh | sh +``` + +You can also pass options to the script like so + +``` +$ curl -sSL drprg.mbh.sh | sh -s -- --help +install.sh [option] + +Fetch and install the latest version of drprg, if drprg is already +installed it will be updated to the latest version. + +Options + -V, --verbose + Enable verbose output for the installer + + -f, -y, --force, --yes + Skip the confirmation prompt during installation + + -p, --platform + Override the platform identified by the installer + + -b, --bin-dir + Override the bin installation directory [default: /usr/local/bin] + + -a, --arch + Override the architecture identified by the installer [default: x86_64] + + -B, --base-url + Override the base URL used for downloading releases [default: https://github.com/mbhall88/drprg/releases] + + -h, --help + Display this help message +``` + + +## Cargo + +[![Crates.io](https://img.shields.io/crates/v/drprg.svg)](https://crates.io/crates/drprg) + +If install via cargo, you must have the [external dependecies](#dependencies) installed separately. + +``` +$ cargo install drprg +``` + ## Local **Minimum supported Rust version**: `1.65.0` @@ -76,7 +133,6 @@ changed by specifying a path to `EXTDIR` when installing the external dependenci $ just deps EXTDIR="some/other/dir" ``` - [pandora]: https://github.com/rmcolq/pandora [mafft]: https://mafft.cbrc.jp/alignment/software/ [makeprg]: https://github.com/leoisl/make_prg/ diff --git a/install/install.sh b/install/install.sh new file mode 100644 index 0000000..3470ce2 --- /dev/null +++ b/install/install.sh @@ -0,0 +1,451 @@ +#!/usr/bin/env sh + +set -eu +printf '\n' + +BOLD="$(tput bold 2>/dev/null || printf '')" +GREY="$(tput setaf 0 2>/dev/null || printf '')" +UNDERLINE="$(tput smul 2>/dev/null || printf '')" +RED="$(tput setaf 1 2>/dev/null || printf '')" +GREEN="$(tput setaf 2 2>/dev/null || printf '')" +YELLOW="$(tput setaf 3 2>/dev/null || printf '')" +BLUE="$(tput setaf 4 2>/dev/null || printf '')" +MAGENTA="$(tput setaf 5 2>/dev/null || printf '')" +NO_COLOR="$(tput sgr0 2>/dev/null || printf '')" +PROJECT="drprg" +GH_USER="mbhall88" + +SUPPORTED_TARGETS="x86_64-unknown-linux-musl x86_64-unknown-linux-gnu \ + aarch64-unknown-linux-musl i686-unknown-linux-musl" + +info() { + printf '%s\n' "${BOLD}${GREY}>${NO_COLOR} $*" +} + +warn() { + printf '%s\n' "${YELLOW}! $*${NO_COLOR}" +} + +error() { + printf '%s\n' "${RED}x $*${NO_COLOR}" >&2 +} + +completed() { + printf '%s\n' "${GREEN}✓${NO_COLOR} $*" +} + +has() { + command -v "$1" 1>/dev/null 2>&1 +} + +# Make sure user is not using zsh or non-POSIX-mode bash, which can cause issues +verify_shell_is_posix_or_exit() { + if [ -n "${ZSH_VERSION+x}" ]; then + error "Running installation script with \`zsh\` is known to cause errors." + error "Please use \`sh\` instead." + exit 1 + elif [ -n "${BASH_VERSION+x}" ] && [ -z "${POSIXLY_CORRECT+x}" ]; then + error "Running installation script with non-POSIX \`bash\` may cause errors." + error "Please use \`sh\` instead." + exit 1 + else + true # No-op: no issues detected + fi +} + +# Gets path to a temporary file, even if +get_tmpfile() { + suffix="$1" + if has mktemp; then + printf "%s.%s" "$(mktemp)" "${suffix}" + else + # No really good options here--let's pick a default + hope + printf "/tmp/${PROJECT}.%s" "${suffix}" + fi +} + +# Makes a temporary directory +get_tmpdir() { + if has mktemp; then + printf "%s" "$(mktemp -d)" + else + # No really good options here--let's pick a default + hope + printf "%s" "/tmp/${PROJECT}" + fi +} + +# Gets the latest github release tag (version) +get_version_from_github() { + ver=$(wget -qO - "https://api.github.com/repos/${GH_USER}/${PROJECT}/releases/latest" | + grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + echo "$ver" +} + +# Test if a location is writeable by trying to write to it. Windows does not let +# you test writeability other than by writing: https://stackoverflow.com/q/1999988 +test_writeable() { + path="${1:-}/test.txt" + if touch "${path}" 2>/dev/null; then + rm "${path}" + return 0 + else + return 1 + fi +} + +download() { + file="$1" + url="$2" + + if has curl; then + cmd="curl --fail --silent --location --output $file $url" + elif has wget; then + cmd="wget --quiet --output-document=$file $url" + elif has fetch; then + cmd="fetch --quiet --output=$file $url" + else + error "No HTTP download program (curl, wget, fetch) found, exiting…" + return 1 + fi + + $cmd && return 0 || rc=$? + + error "Command failed (exit code $rc): ${BLUE}${cmd}${NO_COLOR}" + printf "\n" >&2 + info "This is likely due to $PROJECT not yet supporting your configuration." + info "If you would like to see a build for your configuration," + info "please create an issue requesting a build for ${MAGENTA}${TARGET}${NO_COLOR}:" + info "${BOLD}${UNDERLINE}https://github.com/${GH_USER}/${PROJECT}/issues/new/${NO_COLOR}" + return $rc +} + +unpack() { + archive=$1 + bin_dir=$2 + temp_dir=$(get_tmpdir) + sudo=${3-} + + case "$archive" in + *.tar.gz) + flags=$(test -n "${VERBOSE-}" && echo "-xzvof" || echo "-xzof") + ${sudo} tar "${flags}" "${archive}" -C "${temp_dir}" + ${sudo} mv "${temp_dir}"/${PROJECT}-*/${PROJECT} "${bin_dir}/${PROJECT}" + return 0 + ;; + *.zip) + flags=$(test -z "${VERBOSE-}" && echo "-qqo" || echo "-o") + UNZIP="${flags}" ${sudo} unzip "${archive}" -d "${temp_dir}" + return 0 + ;; + esac + + error "Unknown package extension." + printf "\n" + info "This almost certainly results from a bug in this script--please file a" + info "bug report at https://github.com/${GH_USER}/${PROJECT}/issues" + return 1 +} + +usage() { + printf "%s\n" \ + "install.sh [option]" \ + "" \ + "Fetch and install the latest version of $PROJECT, if $PROJECT is already" \ + "installed it will be updated to the latest version." + + printf "\n%s\n" "Options" + printf "\t%s\n\t\t%s\n\n" \ + "-V, --verbose" "Enable verbose output for the installer" \ + "-f, -y, --force, --yes" "Skip the confirmation prompt during installation" \ + "-p, --platform" "Override the platform identified by the installer [default: ${PLATFORM}]" \ + "-b, --bin-dir" "Override the bin installation directory [default: ${BIN_DIR}]" \ + "-a, --arch" "Override the architecture identified by the installer [default: ${ARCH}]" \ + "-B, --base-url" "Override the base URL used for downloading releases [default: ${BASE_URL}]" \ + "-h, --help" "Display this help message" +} + +elevate_priv() { + if ! has sudo; then + error 'Could not find the command "sudo", needed to get permissions for install.' + info "If you are on Windows, please run your shell as an administrator, then" + info "rerun this script. Otherwise, please run this script as root, or install" + info "sudo." + exit 1 + fi + if ! sudo -v; then + error "Superuser not granted, aborting installation" + exit 1 + fi +} + +install() { + ext="$1" + url="$2" + + if test_writeable "${BIN_DIR}"; then + sudo="" + msg="Installing $PROJECT, please wait…" + else + warn "Escalated permissions are required to install to ${BIN_DIR}" + elevate_priv + sudo="sudo" + msg="Installing $PROJECT as root, please wait…" + fi + info "$msg" + + archive=$(get_tmpfile "$ext") + + # download to the temp file + download "${archive}" "${url}" + + # unpack the temp file to the bin dir, using sudo if required + unpack "${archive}" "${BIN_DIR}" "${sudo}" +} + +# Currently supporting: +# - win (Git Bash) +# - darwin +# - linux +# - linux_musl (Alpine) +# - freebsd +detect_platform() { + platform="$(uname -s | tr '[:upper:]' '[:lower:]')" + + case "${platform}" in + msys_nt*) platform="pc-windows-msvc" ;; + cygwin_nt*) platform="pc-windows-msvc" ;; + # mingw is Git-Bash + mingw*) platform="pc-windows-msvc" ;; + # use the statically compiled musl bins on linux to avoid linking issues. + linux) platform="unknown-linux-musl" ;; + darwin) platform="apple-darwin" ;; + freebsd) platform="unknown-freebsd" ;; + esac + + printf '%s' "${platform}" +} + +# Currently supporting: +# - x86_64 +# - i386 +# - arm +# - arm64 +detect_arch() { + arch="$(uname -m | tr '[:upper:]' '[:lower:]')" + + case "${arch}" in + amd64) arch="x86_64" ;; + armv*) arch="arm" ;; + arm64) arch="aarch64" ;; + esac + + # `uname -m` in some cases mis-reports 32-bit OS as 64-bit, so double check + if [ "${arch}" = "x86_64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then + arch=i686 + elif [ "${arch}" = "aarch64" ] && [ "$(getconf LONG_BIT)" -eq 32 ]; then + arch=arm + fi + + printf '%s' "${arch}" +} + +detect_target() { + arch="$1" + platform="$2" + target="$arch-$platform" + + if [ "${target}" = "arm-unknown-linux-musl" ]; then + target="${target}eabihf" + fi + + printf '%s' "${target}" +} + +confirm() { + if [ -z "${FORCE-}" ]; then + printf "%s " "${MAGENTA}?${NO_COLOR} $* ${BOLD}[y/N]${NO_COLOR}" + set +e + read -r yn &2 + info "If you would like to see a build for your configuration," + info "please create an issue requesting a build for ${MAGENTA}${target}${NO_COLOR}:" + info "${BOLD}${UNDERLINE}https://github.com/${GH_USER}/${PROJECT}/issues/new/${NO_COLOR}" + printf "\n" + exit 1 + fi +} + +# defaults +if [ -z "${PLATFORM-}" ]; then + PLATFORM="$(detect_platform)" +fi + +if [ -z "${BIN_DIR-}" ]; then + BIN_DIR=/usr/local/bin +fi + +if [ -z "${ARCH-}" ]; then + ARCH="$(detect_arch)" +fi + +if [ -z "${BASE_URL-}" ]; then + BASE_URL="https://github.com/${GH_USER}/${PROJECT}/releases" +fi + +# Non-POSIX shells can break once executing code due to semantic differences +verify_shell_is_posix_or_exit + +# parse argv variables +while [ "$#" -gt 0 ]; do + case "$1" in + -p | --platform) + PLATFORM="$2" + shift 2 + ;; + -b | --bin-dir) + BIN_DIR="$2" + shift 2 + ;; + -a | --arch) + ARCH="$2" + shift 2 + ;; + -B | --base-url) + BASE_URL="$2" + shift 2 + ;; + + -V | --verbose) + VERBOSE=1 + shift 1 + ;; + -f | -y | --force | --yes) + FORCE=1 + shift 1 + ;; + -h | --help) + usage + exit + ;; + + -p=* | --platform=*) + PLATFORM="${1#*=}" + shift 1 + ;; + -b=* | --bin-dir=*) + BIN_DIR="${1#*=}" + shift 1 + ;; + -a=* | --arch=*) + ARCH="${1#*=}" + shift 1 + ;; + -B=* | --base-url=*) + BASE_URL="${1#*=}" + shift 1 + ;; + -V=* | --verbose=*) + VERBOSE="${1#*=}" + shift 1 + ;; + -f=* | -y=* | --force=* | --yes=*) + FORCE="${1#*=}" + shift 1 + ;; + + *) + error "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +TARGET="$(detect_target "${ARCH}" "${PLATFORM}")" + +is_build_available "${ARCH}" "${PLATFORM}" "${TARGET}" + +printf " %s\n" "${UNDERLINE}Configuration${NO_COLOR}" +info "${BOLD}Bin directory${NO_COLOR}: ${GREEN}${BIN_DIR}${NO_COLOR}" +info "${BOLD}Platform${NO_COLOR}: ${GREEN}${PLATFORM}${NO_COLOR}" +info "${BOLD}Arch${NO_COLOR}: ${GREEN}${ARCH}${NO_COLOR}" + +# non-empty VERBOSE enables verbose untarring +if [ -n "${VERBOSE-}" ]; then + VERBOSE=v + info "${BOLD}Verbose${NO_COLOR}: yes" +else + VERBOSE= +fi + +printf '\n' + +EXT=tar.gz +if [ "${PLATFORM}" = "pc-windows-msvc" ]; then + EXT=zip +fi + +VERSION=$(get_version_from_github) +URL="${BASE_URL}/latest/download/${PROJECT}-${VERSION}-${TARGET}.${EXT}" +info "Tarball URL: ${UNDERLINE}${BLUE}${URL}${NO_COLOR}" +confirm "Install $PROJECT ${GREEN}v${VERSION} (latest)${NO_COLOR} to ${BOLD}${GREEN}${BIN_DIR}${NO_COLOR}?" +check_bin_dir "${BIN_DIR}" + +install "${EXT}" "$URL" +completed "$PROJECT (v${VERSION}) installed"