From 5961bf000d2a117a91e4c3f4f561b6810e4f8bb5 Mon Sep 17 00:00:00 2001 From: kallookoo Date: Thu, 24 Apr 2025 15:23:34 +0200 Subject: [PATCH 1/4] Refactor install script for improved error handling and OS detection --- install.sh | 218 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 82 deletions(-) diff --git a/install.sh b/install.sh index 3c0c8a9..23724a1 100644 --- a/install.sh +++ b/install.sh @@ -1,108 +1,162 @@ #!/usr/bin/env bash -GH_REPO="axllent/mailpit" -TIMEOUT=90 -INSTALL_PATH="${INSTALL_PATH:-/usr/local/bin}" +# This script will install the latest version of Mailpit. -set -e +# Check dependencias is installed +for cmd in curl tar; do + if ! command -v "$cmd" &>/dev/null; then + echo "Then $cmd command is required but not installed." + echo "Please install $cmd and try again." + exit 1 + fi +done -VERSION=$(curl --silent --fail --location --max-time "${TIMEOUT}" "https://api.github.com/repos/${GH_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') -if [ $? -ne 0 ]; then - echo -ne "\nThere was an error trying to check what is the latest version of Mailpit.\nPlease try again later.\n" - exit 1 -fi - -# detect the platform -OS="$(uname)" -case $OS in -Linux) - OS='linux' - ;; -FreeBSD) - OS='freebsd' - echo 'OS not supported' - exit 2 - ;; -NetBSD) - OS='netbsd' - echo 'OS not supported' - exit 2 - ;; -OpenBSD) - OS='openbsd' - echo 'OS not supported' - exit 2 - ;; -Darwin) - OS='darwin' - ;; -SunOS) - OS='solaris' - echo 'OS not supported' - exit 2 - ;; +# Check if the OS is supported. +OS= +case "$(uname -s)" in +Linux) OS="linux" ;; +Darwin) OS="Darwin" ;; *) - echo 'OS not supported' + echo "OS not supported." exit 2 ;; esac -# detect the arch -OS_type="$(uname -m)" -case "$OS_type" in +# Detect the architecture of the OS. +OS_ARCH= +case "$(uname -m)" in x86_64 | amd64) - OS_type='amd64' + OS_ARCH="amd64" ;; i?86 | x86) - OS_type='386' + OS_ARCH="386" ;; aarch64 | arm64) - OS_type='arm64' + OS_ARCH="arm64" ;; *) - echo 'OS type not supported' + echo "OS architecture not supported." exit 2 ;; esac -GH_REPO_BIN="mailpit-${OS}-${OS_type}.tar.gz" +GH_REPO="axllent/mailpit" +INSTALL_PATH="/usr/local/bin" +TIMEOUT=90 # --max-time in curl -#create tmp directory and move to it with macOS compatibility fallback -tmp_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mailpit-install.XXXXXXXXXX') -cd "$tmp_dir" +# The arguments in extended format for the curl command. +CURL_ARGS=( + "--silent" + "--fail" + "--location" + "--max-time" "$TIMEOUT" +) +# This is used to authenticate with the GitHub API. (Fix the public rate limiting issue) +AUTH_TOKEN="${GITHUB_TOKEN:-}" -echo "Downloading Mailpit $VERSION" -LINK="https://github.com/${GH_REPO}/releases/download/${VERSION}/${GH_REPO_BIN}" +# Update the default values if the user has set. +while [[ $# -gt 0 ]]; do + case $1 in + --install-path) + shift + INSTALL_PATH="$1" + ;; + --auth | --auth-token | --github-token | --token) + shift + [[ "${1:-}" =~ ^- ]] || AUTH_TOKEN="$1" + ;; + *) ;; + esac + shift +done -curl --silent --fail --location --max-time "${TIMEOUT}" "${LINK}" -o "${GH_REPO_BIN}" || { - echo "Error downloading latest release" - exit 2 -} +# Set the header auth if the user has set a GitHub token. +if [[ -n "$AUTH_TOKEN" ]] && [[ "$AUTH_TOKEN" =~ ^gh[pousr]_[A-Za-z0-9_]{36,251}$ ]]; then + CURL_ARGS+=("--header" "Authorization: 'Bearer $AUTH_TOKEN'") +fi -tar zxf "$GH_REPO_BIN" || { - echo "Error extracting ${GH_REPO_BIN}" - exit 2 -} - -mkdir -p "${INSTALL_PATH}" || exit 2 -cp mailpit "${INSTALL_PATH}" || exit 2 -chmod 755 "${INSTALL_PATH}/mailpit" || exit 2 -case "$OS" in -'linux') - if [ "$(id -u)" -eq "0" ]; then - chown root:root "${INSTALL_PATH}/mailpit" || exit 2 +CURL_OUTPUT=$(curl "${CURL_ARGS[@]}" "https://api.github.com/repos/${GH_REPO}/releases/latest") +EXIT_CODE=$? +if [[ $EXIT_CODE -eq 0 ]]; then + # Extracts the latest version using jq, awk, or sed. + if command -v jq &>/dev/null; then + VERSION=$(echo "$CURL_OUTPUT" | jq -r '.tag_name') + elif command -v awk &>/dev/null; then + VERSION=$(echo "$CURL_OUTPUT" | awk -F: '$1 ~ /tag_name/ {gsub(/[^v0-9\.]+/, "", $2) ;print $2; exit}') + else + VERSION=$(echo "$CURL_OUTPUT" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') fi - ;; -'freebsd' | 'openbsd' | 'netbsd' | 'darwin') - if [ "$(id -u)" -eq "0" ]; then - chown root:wheel "${INSTALL_PATH}/mailpit" || exit 2 - fi - ;; -*) - echo 'OS not supported' - exit 2 - ;; -esac +fi -rm -rf "$tmp_dir" -echo "Installed successfully to ${INSTALL_PATH}/mailpit" +# Validate the version. +if ! [[ "$VERSION" =~ ^v[0-9]{1,}[0-9\.]+$ ]]; then + echo "There was an error trying to check what is the latest version of Mailpit." + echo "Please try again later." + exit $EXIT_CODE +fi + +TEMP_DIR="$(mktemp -qd)" +EXIT_CODE=$? +# Ensure the temporary directory exists and is a directory. +if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR" ]; then + echo "ERROR: Creating temporary directory." + exit $EXIT_CODE +fi + +GH_REPO_BIN="mailpit-${OS}-${OS_ARCH}.tar.gz" +CURL_ARGS+=("--output" "${GH_REPO_BIN}") + +if ! cd "$TEMP_DIR"; then + EXIT_CODE=$? + echo "ERROR: Changing to temporary directory." +else + # Download the latest release. + curl "${CURL_ARGS[@]}" "https://github.com/${GH_REPO}/releases/download/${VERSION}/${GH_REPO_BIN}" + EXIT_CODE=$? + + # The following conditions check each step of the installation. + # If there is an error in any of the steps, an error message is printed. + + if ! [[ -f "${GH_REPO_BIN}" ]]; then + echo "ERROR: Downloading latest release." + elif ! tar zxf "$GH_REPO_BIN"; then + EXIT_CODE=$? + echo "ERROR: Extracting \"${GH_REPO_BIN}\"." + elif ! mkdir -p "${INSTALL_PATH}"; then + EXIT_CODE=$? + echo "ERROR: Creating \"${INSTALL_PATH}\" directory." + elif ! cp -f mailpit "${INSTALL_PATH}"; then + EXIT_CODE=$? + echo "ERROR: Copying mailpit to \"${INSTALL_PATH}\" directory." + elif ! chmod 755 "${INSTALL_PATH}/mailpit"; then + EXIT_CODE=$? + echo "ERROR: Setting permissions for \"${INSTALL_PATH}/mailpit\" binary." + fi + + # Set the owner and group to root:root if the script is run as root. + if [ $EXIT_CODE -eq 0 ] && [ "$(id -u)" -eq "0" ]; then + OWNER="root" + GROUP="root" + # Set the OWNER, GROUP variable when the OS not use the default root:root. + case "$OS" in + darwin) GROUP="wheel" ;; + *) ;; + esac + + if ! chown "${OWNER}:${GROUP}" "${INSTALL_PATH}/mailpit"; then + EXIT_CODE=$? + echo "ERROR: Setting ownership for \"${INSTALL_PATH}/mailpit\" binary." + fi + fi +fi + +# Cleanup the temporary directory. +rm -rf "$TEMP_DIR" +# Check the EXIT_CODE variable, and print the success or error message. +if [[ $EXIT_CODE -eq 0 ]]; then + echo "Installed successfully to \"${INSTALL_PATH}/mailpit\"" +else + echo "There was an error installing Mailpit." + echo "Please try again later." +fi +exit $EXIT_CODE From 658c94a2d1408826fc71d0a38c14a17101cb3ad2 Mon Sep 17 00:00:00 2001 From: kallookoo Date: Wed, 30 Apr 2025 18:37:17 +0200 Subject: [PATCH 2/4] Converts to sh for compatibility. --- install.sh | 153 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 103 insertions(+), 50 deletions(-) diff --git a/install.sh b/install.sh index 23724a1..cd8745d 100644 --- a/install.sh +++ b/install.sh @@ -1,10 +1,10 @@ -#!/usr/bin/env bash +#!/bin/sh # This script will install the latest version of Mailpit. # Check dependencias is installed for cmd in curl tar; do - if ! command -v "$cmd" &>/dev/null; then + if ! command -v "$cmd" >/dev/null 2>&1; then echo "Then $cmd command is required but not installed." echo "Please install $cmd and try again." exit 1 @@ -42,58 +42,75 @@ esac GH_REPO="axllent/mailpit" INSTALL_PATH="/usr/local/bin" -TIMEOUT=90 # --max-time in curl - -# The arguments in extended format for the curl command. -CURL_ARGS=( - "--silent" - "--fail" - "--location" - "--max-time" "$TIMEOUT" -) +TIMEOUT=90 # This is used to authenticate with the GitHub API. (Fix the public rate limiting issue) -AUTH_TOKEN="${GITHUB_TOKEN:-}" +# Try the GITHUB_TOKEN environment variable is set globally. +GITHUB_API_TOKEN="${GITHUB_TOKEN:-}" # Update the default values if the user has set. -while [[ $# -gt 0 ]]; do +while [ $# -gt 0 ]; do case $1 in --install-path) shift - INSTALL_PATH="$1" + case "$1" in + */*) + # Remove trailing slashes from the path. + INSTALL_PATH="$(echo "$1" | sed 's#/\+$##')" + [ -z "$INSTALL_PATH" ] && INSTALL_PATH="/" + ;; + esac ;; --auth | --auth-token | --github-token | --token) shift - [[ "${1:-}" =~ ^- ]] || AUTH_TOKEN="$1" + case "$1" in + gh*) + GITHUB_API_TOKEN="$1" + ;; + esac ;; *) ;; esac shift done -# Set the header auth if the user has set a GitHub token. -if [[ -n "$AUTH_TOKEN" ]] && [[ "$AUTH_TOKEN" =~ ^gh[pousr]_[A-Za-z0-9_]{36,251}$ ]]; then - CURL_ARGS+=("--header" "Authorization: 'Bearer $AUTH_TOKEN'") +# Description of the sort parameters for curl command. +# -s: Silent mode. +# -f: Fail silently on server errors. +# -L: Follow redirects. +# -m: Set maximum time allowed for the transfer. + +if [ -n "$GITHUB_API_TOKEN" ] && [ "${#GITHUB_API_TOKEN}" -gt 36 ]; then + CURL_OUTPUT="$(curl -sfL -m $TIMEOUT -H "Authorization: Bearer $GITHUB_API_TOKEN" https://api.github.com/repos/${GH_REPO}/releases/latest)" + EXIT_CODE=$? +else + CURL_OUTPUT="$(curl --sfL -m $TIMEOUT https://api.github.com/repos/${GH_REPO}/releases/latest)" + EXIT_CODE=$? fi -CURL_OUTPUT=$(curl "${CURL_ARGS[@]}" "https://api.github.com/repos/${GH_REPO}/releases/latest") -EXIT_CODE=$? -if [[ $EXIT_CODE -eq 0 ]]; then +VERSION="" +if [ $EXIT_CODE -eq 0 ]; then # Extracts the latest version using jq, awk, or sed. - if command -v jq &>/dev/null; then - VERSION=$(echo "$CURL_OUTPUT" | jq -r '.tag_name') - elif command -v awk &>/dev/null; then + if command -v jq >/dev/null 2>&1; then + # Use jq -n because the output is not a valid JSON in sh. + VERSION=$(jq -n "$CURL_OUTPUT" | jq -r '.tag_name') + elif command -v awk >/dev/null 2>&1; then VERSION=$(echo "$CURL_OUTPUT" | awk -F: '$1 ~ /tag_name/ {gsub(/[^v0-9\.]+/, "", $2) ;print $2; exit}') - else + elif command -v sed >/dev/null 2>&1; then VERSION=$(echo "$CURL_OUTPUT" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p') + else + EXIT_CODE=3 fi fi # Validate the version. -if ! [[ "$VERSION" =~ ^v[0-9]{1,}[0-9\.]+$ ]]; then +case "$VERSION" in +v[0-9][0-9\.]*) ;; +*) echo "There was an error trying to check what is the latest version of Mailpit." echo "Please try again later." exit $EXIT_CODE -fi + ;; +esac TEMP_DIR="$(mktemp -qd)" EXIT_CODE=$? @@ -104,33 +121,64 @@ if [ -z "$TEMP_DIR" ] || [ ! -d "$TEMP_DIR" ]; then fi GH_REPO_BIN="mailpit-${OS}-${OS_ARCH}.tar.gz" -CURL_ARGS+=("--output" "${GH_REPO_BIN}") - -if ! cd "$TEMP_DIR"; then - EXIT_CODE=$? - echo "ERROR: Changing to temporary directory." +if [ "$INSTALL_PATH" = "/" ]; then + INSTALL_BIN_PATH="/mailpit" else + INSTALL_BIN_PATH="${INSTALL_PATH}/mailpit" +fi +cd "$TEMP_DIR" || EXIT_CODE=$? +if [ $EXIT_CODE -eq 0 ]; then # Download the latest release. - curl "${CURL_ARGS[@]}" "https://github.com/${GH_REPO}/releases/download/${VERSION}/${GH_REPO_BIN}" + # + # Description of the sort parameters for curl command. + # -s: Silent mode. + # -f: Fail silently on server errors. + # -L: Follow redirects. + # -m: Set maximum time allowed for the transfer. + # -o: Write output to a file instead of stdout. + curl -sfL -m $TIMEOUT -o "${GH_REPO_BIN}" "https://github.com/${GH_REPO}/releases/download/${VERSION}/${GH_REPO_BIN}" EXIT_CODE=$? # The following conditions check each step of the installation. # If there is an error in any of the steps, an error message is printed. - if ! [[ -f "${GH_REPO_BIN}" ]]; then - echo "ERROR: Downloading latest release." - elif ! tar zxf "$GH_REPO_BIN"; then + if [ $EXIT_CODE -eq 0 ]; then + if ! [ -f "${GH_REPO_BIN}" ]; then + EXIT_CODE=1 + echo "ERROR: Downloading latest release." + fi + fi + + if [ $EXIT_CODE -eq 0 ]; then + tar zxf "$GH_REPO_BIN" EXIT_CODE=$? - echo "ERROR: Extracting \"${GH_REPO_BIN}\"." - elif ! mkdir -p "${INSTALL_PATH}"; then + if [ $EXIT_CODE -ne 0 ]; then + echo "ERROR: Extracting \"${GH_REPO_BIN}\"." + fi + fi + + if [ $EXIT_CODE -eq 0 ] && [ ! -d "$INSTALL_PATH" ]; then + mkdir -p "${INSTALL_PATH}" EXIT_CODE=$? - echo "ERROR: Creating \"${INSTALL_PATH}\" directory." - elif ! cp -f mailpit "${INSTALL_PATH}"; then + if [ $EXIT_CODE -ne 0 ]; then + echo "ERROR: Creating \"${INSTALL_PATH}\" directory." + fi + fi + + if [ $EXIT_CODE -eq 0 ]; then + cp mailpit "$INSTALL_BIN_PATH" EXIT_CODE=$? - echo "ERROR: Copying mailpit to \"${INSTALL_PATH}\" directory." - elif ! chmod 755 "${INSTALL_PATH}/mailpit"; then + if [ $EXIT_CODE -ne 0 ]; then + echo "ERROR: Copying mailpit to \"${INSTALL_PATH}\" directory." + fi + fi + + if [ $EXIT_CODE -eq 0 ]; then + chmod 755 "$INSTALL_BIN_PATH" EXIT_CODE=$? - echo "ERROR: Setting permissions for \"${INSTALL_PATH}/mailpit\" binary." + if [ $EXIT_CODE -ne 0 ]; then + echo "ERROR: Setting permissions for \"$INSTALL_BIN_PATH\" binary." + fi fi # Set the owner and group to root:root if the script is run as root. @@ -143,20 +191,25 @@ else *) ;; esac - if ! chown "${OWNER}:${GROUP}" "${INSTALL_PATH}/mailpit"; then - EXIT_CODE=$? - echo "ERROR: Setting ownership for \"${INSTALL_PATH}/mailpit\" binary." + chown "${OWNER}:${GROUP}" "$INSTALL_BIN_PATH" + EXIT_CODE=$? + if [ $EXIT_CODE -ne 0 ]; then + echo "ERROR: Setting ownership for \"$INSTALL_BIN_PATH\" binary." fi fi +else + echo "ERROR: Changing to temporary directory." + exit $EXIT_CODE fi # Cleanup the temporary directory. rm -rf "$TEMP_DIR" # Check the EXIT_CODE variable, and print the success or error message. -if [[ $EXIT_CODE -eq 0 ]]; then - echo "Installed successfully to \"${INSTALL_PATH}/mailpit\"" -else +if [ $EXIT_CODE -ne 0 ]; then echo "There was an error installing Mailpit." echo "Please try again later." + exit $EXIT_CODE fi -exit $EXIT_CODE + +echo "Installed successfully to \"$INSTALL_BIN_PATH\"." +exit 0 From b4f4b857f3e96313d0be069fdf9b26c164c9c0ac Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 1 May 2025 15:47:52 +1200 Subject: [PATCH 3/4] Minor tweaks to installer --- install.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index cd8745d..7cc1fe8 100644 --- a/install.sh +++ b/install.sh @@ -1,8 +1,8 @@ #!/bin/sh -# This script will install the latest version of Mailpit. +# This script will install the latest release of Mailpit. -# Check dependencias is installed +# Check dependencies is installed for cmd in curl tar; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "Then $cmd command is required but not installed." @@ -83,7 +83,7 @@ if [ -n "$GITHUB_API_TOKEN" ] && [ "${#GITHUB_API_TOKEN}" -gt 36 ]; then CURL_OUTPUT="$(curl -sfL -m $TIMEOUT -H "Authorization: Bearer $GITHUB_API_TOKEN" https://api.github.com/repos/${GH_REPO}/releases/latest)" EXIT_CODE=$? else - CURL_OUTPUT="$(curl --sfL -m $TIMEOUT https://api.github.com/repos/${GH_REPO}/releases/latest)" + CURL_OUTPUT="$(curl -sfL -m $TIMEOUT https://api.github.com/repos/${GH_REPO}/releases/latest)" EXIT_CODE=$? fi @@ -207,7 +207,6 @@ rm -rf "$TEMP_DIR" # Check the EXIT_CODE variable, and print the success or error message. if [ $EXIT_CODE -ne 0 ]; then echo "There was an error installing Mailpit." - echo "Please try again later." exit $EXIT_CODE fi From 5e1a22832834ca2cf2fafca48f41c216729ffcea Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 1 May 2025 15:57:57 +1200 Subject: [PATCH 4/4] Allow INSTALL_PATH to be overridden by environment variable --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index 7cc1fe8..f7ac2c9 100644 --- a/install.sh +++ b/install.sh @@ -41,7 +41,7 @@ aarch64 | arm64) esac GH_REPO="axllent/mailpit" -INSTALL_PATH="/usr/local/bin" +INSTALL_PATH="${INSTALL_PATH:-/usr/local/bin}" TIMEOUT=90 # This is used to authenticate with the GitHub API. (Fix the public rate limiting issue) # Try the GITHUB_TOKEN environment variable is set globally.