Improve Linux/Mac install script: warn if .shellrc is r/o, less aggressively append $PATH and be less chatty.

This commit is contained in:
Sebastian Jeltsch 2025-12-17 10:37:15 +01:00
parent 2073165810
commit c3aaac26dc

View file

@ -1,5 +1,5 @@
# Install script for: https://github.com/trailbaseio/trailbase generated by
# https://instl.sh
# Install script for: https://github.com/trailbaseio/trailbase originally
# generated using https://instl.sh. License note at the end.
#
# To use this install script, run the following command:
#
@ -12,11 +12,9 @@
# Windows:
# iwr instl.sh/trailbaseio/trailbase/windows | iex
# Import libraries
# --- Sourced from file: ./lib/colors.sh ---
# Foreground Colors
fRed() {
printf "\e[31m%s\e[0m" "$1"
}
@ -81,88 +79,12 @@ fGrayLight() {
printf "\e[37m%s\e[0m" "$1"
}
## Background Colors
bRed() {
printf "\e[41m%s\e[0m" "$1"
}
bRedLight() {
printf "\e[101m%s\e[0m" "$1"
}
bYellow() {
printf "\e[43m%s\e[0m" "$1"
}
bYellowLight() {
printf "\e[103m%s\e[0m" "$1"
}
bGreen() {
printf "\e[42m%s\e[0m" "$1"
}
bGreenLight() {
printf "\e[102m%s\e[0m" "$1"
}
bBlue() {
printf "\e[44m%s\e[0m" "$1"
}
bBlueLight() {
printf "\e[104m%s\e[0m" "$1"
}
bMagenta() {
printf "\e[45m%s\e[0m" "$1"
}
bMagentaLight() {
printf "\e[105m%s\e[0m" "$1"
}
bCyan() {
printf "\e[46m%s\e[0m" "$1"
}
bCyanLight() {
printf "\e[106m%s\e[0m" "$1"
}
bWhite() {
printf "\e[47m%s\e[0m" "$1"
}
bBlack() {
printf "\e[40m%s\e[0m" "$1"
}
bGray() {
printf "\e[100m%s\e[0m" "$1"
}
bGrayLight() {
printf "\e[37m%s\e[0m" "$1"
}
# Special Colors
resetColor() {
printf "\e[0m"
}
# Theme
primaryColor() {
fCyan "$1"
}
secondaryColor() {
fMagentaLight "$1"
}
# Logging
info() {
fBlueLight " i " && resetColor && fBlue "$1" && echo
}
@ -211,13 +133,11 @@ map_keys() {
# --- End of ./lib/map.sh ---
# Setup variables
verbose="false"
if [ "$verbose" = "true" ]; then
verbose=true
verbose=true
else
verbose=false
verbose=false
fi
# Pass variables from the go server into the script
@ -229,12 +149,15 @@ verbose "Creating temporary directory"
tmpDir="$(mktemp -d)"
verbose "Temporary directory: $tmpDir"
binaryLocation="$HOME/.local/bin"
relativeLocation=".local/bin"
binaryLocation="$HOME/$relativeLocation"
verbose "Binary location: $binaryLocation"
mkdir -p "$binaryLocation"
installLocation="$HOME/.local/bin/.instl/$repo"
installLocation="$HOME/$relativeLocation/.$repo"
verbose "Install location: $installLocation"
# Remove installLocation directory if it exists
if [ -d "$installLocation" ]; then
verbose "Removing existing install location"
@ -242,28 +165,17 @@ if [ -d "$installLocation" ]; then
fi
mkdir -p "$installLocation"
# Print "INSTL" header
# --- Sourced from file: ../shared/intro.ps1 ---
fBlueLight 'Instl is an installer for GitHub Projects'
echo ""
fBlue ' > https://instl.sh'
echo ""
# --- End of ../shared/intro.ps1 ---
# Installation
curlOpts=("-sSL" "--retry" "5" "--retry-delay" "2" "--retry-max-time" "15")
if [ -n "$GH_TOKEN" ]; then
verbose "Using authentication with GH_TOKEN"
curlOpts+=("--header" "Authorization: Bearer $GH_TOKEN")
verbose "Using authentication with GH_TOKEN"
curlOpts+=("--header" "Authorization: Bearer $GH_TOKEN")
elif [ -n "$GITHUB_TOKEN" ]; then
verbose "Using authentication with GITHUB_TOKEN"
curlOpts+=("--header" "Authorization: Bearer $GITHUB_TOKEN")
verbose "Using authentication with GITHUB_TOKEN"
curlOpts+=("--header" "Authorization: Bearer $GITHUB_TOKEN")
else
verbose "No authentication"
verbose "No authentication"
fi
# GitHub public API
@ -277,10 +189,8 @@ tagName="$(echo "$releaseJSON" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1
info "Found latest release of $repo (version: $tagName)"
# Get list of assets
verbose "Parsing release json"
assets=$(echo "$releaseJSON" | grep "browser_download_url" | sed -E 's/.*"([^"]+)".*/\1/')
verbose "Found assets:"
verbose "$assets"
verbose "Found assets: $assets"
assetCount="$(echo "$assets" | wc -l | sed -E 's/^[[:space:]]*//')"
info "Found $assetCount assets in '$tagName' - searching for one that fits your system..."
@ -299,16 +209,16 @@ aarch64=("aarch64" "arm64")
currentArchAliases=()
# Set the right aliases for current host system
if [ "$arch" == "x86_64" ]; then
currentArchAliases=("${amd64[@]}")
currentArchAliases=("${amd64[@]}")
elif [ "$arch" == "i386" ] || [ "$arch" == "i686" ]; then
currentArchAliases=("${amd32[@]}")
currentArchAliases=("${amd32[@]}")
elif [[ "$arch" = "aarch64" ]]; then
currentArchAliases=("${aarch64[@]}")
# Else if starts with "arm"
currentArchAliases=("${aarch64[@]}")
# Else if starts with "arm"
elif [[ "$arch" =~ ^arm ]]; then
currentArchAliases=("${arm[@]}")
currentArchAliases=("${arm[@]}")
else
error "Unsupported architecture: $arch"
error "Unsupported architecture: $arch"
fi
verbose "Current architecture aliases: ${currentArchAliases[*]}"
@ -325,57 +235,57 @@ darwin=("darwin" "macos" "osx")
currentOsAliases=()
# If current os is linux, add linux aliases to the curentOsAliases array
if [ "${os}" == "linux" ]; then
currentOsAliases+=("${linux[@]}")
currentOsAliases+=("${linux[@]}")
elif [ "${os}" == "darwin" ]; then
currentOsAliases+=("${darwin[@]}")
currentOsAliases+=("${darwin[@]}")
fi
verbose "Current operating system aliases: ${currentOsAliases[*]}"
# Create map of assets and a score
for asset in $assets; do
score=0
score=0
# Get file name from asset path
fileName="$(echo "$asset" | awk -F'/' '{ print $NF; }')"
# Set filename to lowercase
fileName="$(echo "$fileName" | tr '[:upper:]' '[:lower:]')"
# Get file name from asset path
fileName="$(echo "$asset" | awk -F'/' '{ print $NF; }')"
# Set filename to lowercase
fileName="$(echo "$fileName" | tr '[:upper:]' '[:lower:]')"
# Set score to one, if the file name contains the current os
for osAlias in "${currentOsAliases[@]}"; do
if [[ "${fileName}" == *"$osAlias"* ]]; then
score=10
break
fi
done
# Set score to one, if the file name contains the current os
for osAlias in "${currentOsAliases[@]}"; do
if [[ "${fileName}" == *"$osAlias"* ]]; then
score=10
break
fi
done
# Add two to the score for every alias that matches the current architecture
for archAlias in "${currentArchAliases[@]}"; do
if [[ "${fileName}" == *"$archAlias"* ]]; then
verbose "Adding one to score for asset $fileName because it matches architecture $archAlias"
score=$((score + 2))
fi
done
# Add two to the score for every alias that matches the current architecture
for archAlias in "${currentArchAliases[@]}"; do
if [[ "${fileName}" == *"$archAlias"* ]]; then
verbose "Adding one to score for asset $fileName because it matches architecture $archAlias"
score=$((score + 2))
fi
done
# Add one to the score if the file name contains .tar or .tar.gz or .tar.bz2
if [[ "$fileName" == *".tar" ]] || [[ "$fileName" == *".tar.gz" ]] || [[ "$fileName" == *".tar.bz2" ]]; then
verbose "Adding one to score for asset $fileName because it is a .tar or .tar.gz or .tar.bz2 file"
score=$((score + 1))
fi
# Add one to the score if the file name contains .tar or .tar.gz or .tar.bz2
if [[ "$fileName" == *".tar" ]] || [[ "$fileName" == *".tar.gz" ]] || [[ "$fileName" == *".tar.bz2" ]]; then
verbose "Adding one to score for asset $fileName because it is a .tar or .tar.gz or .tar.bz2 file"
score=$((score + 1))
fi
# Add two to the score if the file name contains the repo name
if [[ "$fileName" == *"$repo"* ]]; then
verbose "Adding two to score for asset $fileName because it contains the repo name"
score=$((score + 2))
fi
# Add two to the score if the file name contains the repo name
if [[ "$fileName" == *"$repo"* ]]; then
verbose "Adding two to score for asset $fileName because it contains the repo name"
score=$((score + 2))
fi
# Add one to the score if the file name is exactly the repo name
if [[ "$fileName" == "$repo" ]]; then
verbose "Adding one to score for asset $fileName because it is exactly the repo name"
score=$((score + 1))
fi
# Add one to the score if the file name is exactly the repo name
if [[ "$fileName" == "$repo" ]]; then
verbose "Adding one to score for asset $fileName because it is exactly the repo name"
score=$((score + 1))
fi
# Initialize asset with score
map_put assets "$fileName" "$score"
# Initialize asset with score
map_put assets "$fileName" "$score"
done
# Get map entry with highest score
@ -383,25 +293,25 @@ verbose "Finding asset with highest score"
maxScore=0
maxKey=""
for asset in $(map_keys assets); do
score="$(map_get assets "$asset")"
if [ $score -gt $maxScore ]; then
maxScore=$score
maxKey=$asset
fi
verbose "Asset: $asset, score: $score"
score="$(map_get assets "$asset")"
if [ $score -gt $maxScore ]; then
maxScore=$score
maxKey=$asset
fi
verbose "Asset: $asset, score: $score"
done
assetName="$maxKey"
# Check if asset name is still empty
if [ -z "$assetName" ]; then
error "Could not find any assets that fit your system"
error "Could not find any assets that fit your system"
fi
# Get asset URL from release assets
assetURL="$(echo "$assets" | grep -i "$assetName")"
info "Found asset with highest match score: $assetName"
info "Found best match: $assetName"
info "Downloading asset..."
# Download asset
@ -412,125 +322,125 @@ curl "${downloadAssetArgs[@]}"
# Unpack asset if it is compressed
if [[ "$assetName" == *".tar" ]]; then
verbose "Unpacking .tar asset to $tmpDir"
tar -xf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
verbose "Unpacking .tar asset to $tmpDir"
tar -xf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
elif [[ "$assetName" == *".tar.gz" ]]; then
verbose "Unpacking .tar.gz asset to $tmpDir"
tar -xzf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
verbose "Unpacking .tar.gz asset to $tmpDir"
tar -xzf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
elif [[ "$assetName" == *".gz" ]]; then
verbose "Unpacking .gz asset to $tmpDir/$repo"
gunzip -c "$tmpDir/$assetName" >"$tmpDir/$repo"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
verbose "Setting asset name to $repo, because it is a .gz file"
assetName="$repo"
verbose "Marking asset as executable"
chmod +x "$tmpDir/$repo"
verbose "Unpacking .gz asset to $tmpDir/$repo"
gunzip -c "$tmpDir/$assetName" >"$tmpDir/$repo"
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
verbose "Setting asset name to $repo, because it is a .gz file"
assetName="$repo"
verbose "Marking asset as executable"
chmod +x "$tmpDir/$repo"
elif [[ "$assetName" == *".tar.bz2" ]]; then
verbose "Unpacking .tar.bz2 asset to $tmpDir"
tar -xjf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset"
rm "$tmpDir/$assetName"
verbose "Unpacking .tar.bz2 asset to $tmpDir"
tar -xjf "$tmpDir/$assetName" -C "$tmpDir"
verbose "Removing packed asset"
rm "$tmpDir/$assetName"
elif [[ "$assetName" == *".zip" ]]; then
verbose "Unpacking .zip asset to $tmpDir/$repo"
unzip "$tmpDir/$assetName" -d "$tmpDir" >/dev/null 2>&1
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
verbose "Unpacking .zip asset to $tmpDir/$repo"
unzip "$tmpDir/$assetName" -d "$tmpDir" >/dev/null 2>&1
verbose "Removing packed asset ($tmpDir/$assetName)"
rm "$tmpDir/$assetName"
else
verbose "Asset is not a tar or zip file. Skipping unpacking."
verbose "Asset is not a tar or zip file. Skipping unpacking."
fi
# If it was unpacked to a single directory, move the files to the root of the tmpDir
# Also check that there are not other non directory files in the tmpDir
verbose "Checking if asset was unpacked to a single directory"
if [ "$(ls -d "$tmpDir"/* | wc -l)" -eq 1 ] && [ -d "$(ls -d "$tmpDir"/*)" ]; then
verbose "Asset was unpacked to a single directory"
verbose "Moving files to root of tmpDir"
mv "$tmpDir"/*/* "$tmpDir"
verbose "Asset was unpacked to a single directory"
verbose "Moving files to root of tmpDir"
mv "$tmpDir"/*/* "$tmpDir"
else
verbose "Asset was not unpacked to a single directory"
verbose "Asset was not unpacked to a single directory"
fi
# Copy files to install location
info "Installing '$repo'"
# Copy files to install location
verbose "Copying files to install location"
# verbose print tmpDir files
verbose "Files in $tmpDir:"
verbose "$(ls "$tmpDir")"
verbose "Files in $tmpDir: $(ls "$tmpDir")"
cp -r "$tmpDir"/* "$installLocation"
# Find binary in install location to symlink to it later
verbose "Finding binary in install location"
binary=""
for file in "$installLocation"/*; do
# Check if the file is a binary file
verbose "Checking if $file is a binary file"
if [ -f "$file" ] && [ -x "$file" ]; then
binary="$file"
verbose "Found binary: $binary"
break
fi
# Check if the file is a binary file
verbose "Checking if $file is a binary file"
if [ -f "$file" ] && [ -x "$file" ]; then
binary="$file"
verbose "Found binary: $binary"
break
fi
done
# Get name of binary
binaryName="$(basename "$binary")"
verbose "Binary name: $binaryName"
# Check if binary is empty
if [ -z "$binary" ]; then
error "Could not find binary in install location"
error "Could not find binary in install location"
fi
# Remove previous symlink if it exists
verbose "Removing previous symlink"
rm "$binaryLocation/$repo" >/dev/null 2>&1 || true
# Get name of binary
binaryName="$(basename "$binary")"
# Create symlink to binary
verbose "Creating symlink '$binaryLocation/$binaryName' -> '$binary'"
ln -s "$binary" "$binaryLocation/$binaryName"
ln -s -f "$binary" "$binaryLocation/$binaryName"
# Add binaryLocation to PATH, if it is not already in PATH
if ! echo "$PATH" | grep -q "$binaryLocation"; then
verbose "Adding $binaryLocation to PATH"
# Array of common shell configuration files
config_files=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.zshrc" "$HOME/.profile")
for config in "${config_files[@]}"; do
# Check if the file exists
if [ -f "$config" ]; then
# Check if binaryLocation is already in the file
if ! grep -q -s "export PATH=$binaryLocation:\$PATH" "$config"; then
verbose "Appending $binaryLocation to $config"
echo "" >>"$config"
echo "# Instl.sh installer binary location" >>"$config"
echo "export PATH=$binaryLocation:\$PATH" >>"$config"
else
verbose "$binaryLocation is already in $config"
fi
else
verbose "$config does not exist. Skipping append."
fi
done
if ! echo "$PATH" | grep -q "\(\$HOME\|$HOME\)/$relativeLocation"; then
verbose "Adding $binaryLocation to PATH"
# Array of common shell configuration files
config_files=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.zshrc" "$HOME/.profile")
for config in "${config_files[@]}"; do
# Check if the file exists
if [ ! -f "$config" ]; then
verbose "$config does not exist. Skipping append."
continue
fi
# Check if binaryLocation is already in the file
if grep -q -s "PATH=.*\(\$HOME\|$HOME\)/$relativeLocation" "$config"; then
verbose "$binaryLocation is already in $config"
continue
fi
if [ ! -w "$config" ]; then
warning "Cannot append PATH: $config is write-protected. Consider adding 'export PATH=\$PATH:$binaryLocation' manually."
continue
fi
verbose "Appending \$HOME/$relativeLocation to $config"
echo "" >>"$config"
echo "export PATH=\$HOME/$relativeLocation:\$PATH" >>"$config"
done
info "You may have to restart your terminal session for the changes to take effect"
else
verbose "$binaryLocation is already in PATH"
verbose "$binaryLocation is already in PATH"
fi
info "Running clean up..."https://github.com/installer/instl
verbose "Removing temporary directory"
info "Cleaning up download directory"
rm -rf "$tmpDir"
# Test if binary exists
if [ ! -f "$binary" ]; then
error "Binary does not exist in installation location"
else
verbose "Binary exists in installation location"
error "Binary does not exist in installation location"
fi
echo
success "You can now run '$binaryName' in your terminal!"
info "You might have to restart your terminal session for the changes to take effect"
##################################################################################