Open coolaj86 opened 1 year ago
echo
with printf
[[
with POSIX [
sed
(I think I got it right)if
pattern matching with case
my_ifs="${IFS}"
IFS='
'
local
variables|| :
? (search fail on that one, of course)pipefail
required?(actual diff
below)
#!/bin/sh
#shellcheck disable=SC2059
set -e
set -u
if [ "${OS:-}" = Windows_NT ]; then
echo 'error: Please install bun using Windows Subsystem for Linux'
exit 1
fi
# Reset
Color_Off=''
# Regular Colors
Red=''
Green=''
Dim='' # White
# Bold
Bold_White=''
Bold_Green=''
if [ -t 1 ]; then
# Reset
Color_Off='\033[0m' # Text Reset
# Regular Colors
Red='\033[0;31m' # Red
Green='\033[0;32m' # Green
Dim='\033[0;2m' # White
# Bold
Bold_Green='\033[1;32m' # Bold Green
Bold_White='\033[1m' # Bold White
fi
error() {
printf "${Red}error${Color_Off}: $*\n" >&2
exit 1
}
info() {
printf "${Dim}$* ${Color_Off}\n"
}
info_bold() {
printf "${Bold_White}$* ${Color_Off}\n"
}
success() {
printf "${Green}$* ${Color_Off}\n"
}
if ! command -v unzip > /dev/null; then
error 'unzip is required to install bun (see: https://github.com/oven-sh/bun#unzip-is-required)'
fi
if [ "$#" -gt 2 ]; then
error 'Too many arguments, only 2 are allowed. The first can be a specific tag of bun to install. (e.g. "bun-v0.1.4") The second can be a build variant of bun to install. (e.g. "debug-info")'
fi
case $(uname -ms) in
'Darwin x86_64')
target=darwin-x64
;;
'Darwin arm64')
target=darwin-aarch64
;;
'Linux aarch64' | 'Linux arm64')
target=linux-aarch64
;;
'Linux x86_64' | *)
target=linux-x64
;;
esac
if [ "$target" = darwin-x64 ]; then
# Is this process running in Rosetta?
# redirect stderr to devnull to avoid error message when not running in Rosetta
if [ "$(sysctl -n sysctl.proc_translated 2> /dev/null)" = 1 ]; then
target=darwin-aarch64
info "Your shell is running in Rosetta 2. Downloading bun for $target instead"
fi
fi
GITHUB=${GITHUB-"https://github.com"}
github_repo="$GITHUB/oven-sh/bun"
if [ "$target" = darwin-x64 ]; then
# If AVX2 isn't supported, use the -baseline build
if [ "$(sysctl -a | grep machdep.cpu | grep AVX2)" = '' ]; then
target=darwin-x64-baseline
fi
fi
if [ $target = linux-x64 ]; then
# If AVX2 isn't supported, use the -baseline build
if ! grep -q avx2 /proc/cpuinfo; then
target=linux-x64-baseline
fi
fi
exe_name=bun
if [ "$#" = 2 ] && [ "$2" = debug-info ]; then
target=$target-profile
exe_name=bun-profile
info "You requested a debug build of bun. More infomation will be shown if a crash occurs."
fi
if [ "$#" = 0 ]; then
bun_uri=$github_repo/releases/latest/download/bun-$target.zip
else
bun_uri=$github_repo/releases/download/$1/bun-$target.zip
fi
#TODO I don't understand why there was a layer of indirection here
install_env=BUN_INSTALL
install_dir="${BUN_INSTALL:-"${HOME}/.bun"}"
bin_dir="${install_dir}/bin"
bin_env="$(basename "${bin_dir}")"
exe="${bin_dir}/bun"
if [ ! -d "$bin_dir" ]; then
mkdir -p "$bin_dir" ||
error "Failed to create install directory \"$bin_dir\""
fi
curl --fail --location --progress-bar --output "$exe.zip" "$bun_uri" ||
error "Failed to download bun from \"$bun_uri\""
unzip -oqd "$bin_dir" "$exe.zip" ||
error 'Failed to extract bun'
mv "$bin_dir/bun-$target/$exe_name" "$exe" ||
error 'Failed to move extracted bun to destination'
chmod +x "$exe" ||
error 'Failed to set permissions on bun executable'
rm -r "$bin_dir/bun-$target" "$exe.zip"
tildify() {
case ${1} in
"$HOME/"*)
echo "${1}" | sed "s:$HOME/:~/:"
;;
*)
echo "$1"
;;
esac
}
success "bun was installed successfully to $Bold_Green$(tildify "$exe")"
# TODO not sure if this is correct. I'm not familiar with || :
if command -v bun > /dev/null; then
# Install completions, but we don't care if it fails
IS_BUN_AUTO_UPDATE=true $exe completions > /dev/null 2>&1 || :
echo "Run 'bun --help' to get started"
exit
fi
refresh_command=''
tilde_bin_dir="$(tildify "$bin_dir")"
quoted_install_dir="$(printf "%s" "$tilde_bin_dir" | sed 's:":\\":')"
# TODO not sure if I understood the intent here
case $quoted_install_dir in
"\"$HOME"/*)
quoted_install_dir=$(printf "%s" "${quoted_install_dir}" | sed 's:/$::')
;;
*) ;;
esac
echo
case $(basename "$SHELL") in
fish)
# Install completions, but we don't care if it fails
IS_BUN_AUTO_UPDATE=true SHELL=fish $exe completions > /dev/null 2>&1 || :
export_install_env="set --export $install_env $quoted_install_dir"
export_path="set --export PATH $bin_env \$PATH"
fish_config=$HOME/.config/fish/config.fish
tilde_fish_config=$(tildify "$fish_config")
if [ -w "$fish_config" ]; then
{
printf '\n# bun'
echo "$export_install_env"
echo "$export_path"
} >> "$fish_config"
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_fish_config\""
refresh_command="source $tilde_fish_config"
else
echo "Manually add the directory to $tilde_fish_config (or similar):"
info_bold " $export_install_env"
info_bold " $export_path"
fi
;;
zsh)
# Install completions, but we don't care if it fails
IS_BUN_AUTO_UPDATE=true SHELL=zsh $exe completions > /dev/null 2>&1 || true
export_install_env="export $install_env=$quoted_install_dir"
export_path="export PATH=\"$bin_env:\$PATH\""
zsh_config=$HOME/.zshrc
tilde_zsh_config=$(tildify "$zsh_config")
if [ -w "$zsh_config" ]; then
{
printf '\n# bun'
echo "$export_install_env"
echo "$export_path"
} >> "$zsh_config"
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_zsh_config\""
refresh_command="exec $SHELL"
else
echo "Manually add the directory to $tilde_zsh_config (or similar):"
info_bold " $export_install_env"
info_bold " $export_path"
fi
;;
bash)
export_install_env="export $install_env=$quoted_install_dir"
export_path="export PATH=$bin_env:\$PATH"
bash_configs="$HOME/.bashrc"
bash_configs="${bash_configs}\n$HOME/.bash_profile"
if [ -n "${XDG_CONFIG_HOME:-}" ]; then
bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/.bash_profile"
bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/.bashrc"
bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/bash_profile"
bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/bashrc"
fi
set_manually=true
my_ifs="${IFS}"
IFS='
'
for bash_config in ${bash_configs}; do
tilde_bash_config=$(tildify "$bash_config")
if [ -w "$bash_config" ]; then
{
printf '\n# bun'
echo "$export_install_env"
echo "$export_path"
} >> "$bash_config"
info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_bash_config\""
refresh_command="source $bash_config"
set_manually=false
break
fi
done
IFS="${my_ifs}"
if [ "$set_manually" = true ]; then
tilde_bash_config=$(tildify "$HOME/.bashrc")
echo "Manually add the directory to $tilde_bash_config (or similar):"
info_bold " $export_install_env"
info_bold " $export_path"
fi
;;
*)
echo 'Manually add the directory to ~/.bashrc (or similar):'
info_bold " export $install_env=$quoted_install_dir"
info_bold " export PATH=\"$bin_env:\$PATH\""
;;
esac
echo
info "To get started, run:"
echo
if [ -n "$refresh_command" ]; then
info_bold " $refresh_command"
fi
info_bold " bun --help"
1,2c1,4
< #!/usr/bin/env bash
< set -euo pipefail
---
> #!/bin/sh
> #shellcheck disable=SC2059
> set -e
> set -u
4c6
< if [[ ${OS:-} = Windows_NT ]]; then
---
> if [ "${OS:-}" = Windows_NT ]; then
21c23
< if [[ -t 1 ]]; then
---
> if [ -t 1 ]; then
36c38
< echo -e "${Red}error${Color_Off}:" "$@" >&2
---
> printf "${Red}error${Color_Off}: $*\n" >&2
41c43
< echo -e "${Dim}$@ ${Color_Off}"
---
> printf "${Dim}$* ${Color_Off}\n"
45c47
< echo -e "${Bold_White}$@ ${Color_Off}"
---
> printf "${Bold_White}$* ${Color_Off}\n"
49c51
< echo -e "${Green}$@ ${Color_Off}"
---
> printf "${Green}$* ${Color_Off}\n"
52c54
< command -v unzip >/dev/null ||
---
> if ! command -v unzip > /dev/null; then
53a56
> fi
55c58
< if [[ $# -gt 2 ]]; then
---
> if [ "$#" -gt 2 ]; then
74c77
< if [[ $target = darwin-x64 ]]; then
---
> if [ "$target" = darwin-x64 ]; then
77c80
< if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then
---
> if [ "$(sysctl -n sysctl.proc_translated 2> /dev/null)" = 1 ]; then
87c90
< if [[ $target = darwin-x64 ]]; then
---
> if [ "$target" = darwin-x64 ]; then
89c92
< if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
---
> if [ "$(sysctl -a | grep machdep.cpu | grep AVX2)" = '' ]; then
94c97
< if [[ $target = linux-x64 ]]; then
---
> if [ $target = linux-x64 ]; then
96c99
< if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
---
> if ! grep -q avx2 /proc/cpuinfo; then
103c106
< if [[ $# = 2 && $2 = debug-info ]]; then
---
> if [ "$#" = 2 ] && [ "$2" = debug-info ]; then
109c112
< if [[ $# = 0 ]]; then
---
> if [ "$#" = 0 ]; then
114a118
> #TODO I don't understand why there was a layer of indirection here
116d119
< bin_env=\$$install_env/bin
118,120c121,124
< install_dir=${!install_env:-$HOME/.bun}
< bin_dir=$install_dir/bin
< exe=$bin_dir/bun
---
> install_dir="${BUN_INSTALL:-"${HOME}/.bun"}"
> bin_dir="${install_dir}/bin"
> bin_env="$(basename "${bin_dir}")"
> exe="${bin_dir}/bun"
122c126
< if [[ ! -d $bin_dir ]]; then
---
> if [ ! -d "$bin_dir" ]; then
142,146c146,150
< if [[ $1 = $HOME/* ]]; then
< local replacement=\~/
<
< echo "${1/$HOME\//$replacement}"
< else
---
> case ${1} in
> "$HOME/"*)
> echo "${1}" | sed "s:$HOME/:~/:"
> ;;
> *)
148c152,153
< fi
---
> ;;
> esac
153c158,159
< if command -v bun >/dev/null; then
---
> # TODO not sure if this is correct. I'm not familiar with || :
> if command -v bun > /dev/null; then
155c161
< IS_BUN_AUTO_UPDATE=true $exe completions &>/dev/null || :
---
> IS_BUN_AUTO_UPDATE=true $exe completions > /dev/null 2>&1 || :
163,164c169,170
< tilde_bin_dir=$(tildify "$bin_dir")
< quoted_install_dir=\"${install_dir//\"/\\\"}\"
---
> tilde_bin_dir="$(tildify "$bin_dir")"
> quoted_install_dir="$(printf "%s" "$tilde_bin_dir" | sed 's:":\\":')"
166,168c172,177
< if [[ $quoted_install_dir = \"$HOME/* ]]; then
< quoted_install_dir=${quoted_install_dir/$HOME\//\$HOME/}
< fi
---
> # TODO not sure if I understood the intent here
> case $quoted_install_dir in
> "\"$HOME"/*)
> quoted_install_dir=$(printf "%s" "${quoted_install_dir}" | sed 's:/$::')
> ;;
> *) ;;
169a179,180
> esac
>
175c186
< IS_BUN_AUTO_UPDATE=true SHELL=fish $exe completions &>/dev/null || :
---
> IS_BUN_AUTO_UPDATE=true SHELL=fish $exe completions > /dev/null 2>&1 || :
177,180c188,189
< commands=(
< "set --export $install_env $quoted_install_dir"
< "set --export PATH $bin_env \$PATH"
< )
---
> export_install_env="set --export $install_env $quoted_install_dir"
> export_path="set --export PATH $bin_env \$PATH"
185c194
< if [[ -w $fish_config ]]; then
---
> if [ -w "$fish_config" ]; then
187c196
< echo -e '\n# bun'
---
> printf '\n# bun'
189,192c198,200
< for command in "${commands[@]}"; do
< echo "$command"
< done
< } >>"$fish_config"
---
> echo "$export_install_env"
> echo "$export_path"
> } >> "$fish_config"
200,202c208,209
< for command in "${commands[@]}"; do
< info_bold " $command"
< done
---
> info_bold " $export_install_env"
> info_bold " $export_path"
207c214
< IS_BUN_AUTO_UPDATE=true SHELL=zsh $exe completions &>/dev/null || :
---
> IS_BUN_AUTO_UPDATE=true SHELL=zsh $exe completions > /dev/null 2>&1 || true
209,212c216,217
< commands=(
< "export $install_env=$quoted_install_dir"
< "export PATH=\"$bin_env:\$PATH\""
< )
---
> export_install_env="export $install_env=$quoted_install_dir"
> export_path="export PATH=\"$bin_env:\$PATH\""
217c222
< if [[ -w $zsh_config ]]; then
---
> if [ -w "$zsh_config" ]; then
219c224
< echo -e '\n# bun'
---
> printf '\n# bun'
221,224c226,228
< for command in "${commands[@]}"; do
< echo "$command"
< done
< } >>"$zsh_config"
---
> echo "$export_install_env"
> echo "$export_path"
> } >> "$zsh_config"
232,234c236,237
< for command in "${commands[@]}"; do
< info_bold " $command"
< done
---
> info_bold " $export_install_env"
> info_bold " $export_path"
238,241c241,242
< commands=(
< "export $install_env=$quoted_install_dir"
< "export PATH=$bin_env:\$PATH"
< )
---
> export_install_env="export $install_env=$quoted_install_dir"
> export_path="export PATH=$bin_env:\$PATH"
243,246c244,245
< bash_configs=(
< "$HOME/.bashrc"
< "$HOME/.bash_profile"
< )
---
> bash_configs="$HOME/.bashrc"
> bash_configs="${bash_configs}\n$HOME/.bash_profile"
248,254c247,251
< if [[ ${XDG_CONFIG_HOME:-} ]]; then
< bash_configs+=(
< "$XDG_CONFIG_HOME/.bash_profile"
< "$XDG_CONFIG_HOME/.bashrc"
< "$XDG_CONFIG_HOME/bash_profile"
< "$XDG_CONFIG_HOME/bashrc"
< )
---
> if [ -n "${XDG_CONFIG_HOME:-}" ]; then
> bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/.bash_profile"
> bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/.bashrc"
> bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/bash_profile"
> bash_configs="${bash_configs}\n$XDG_CONFIG_HOME/bashrc"
258c255,258
< for bash_config in "${bash_configs[@]}"; do
---
> my_ifs="${IFS}"
> IFS='
> '
> for bash_config in ${bash_configs}; do
261c261
< if [[ -w $bash_config ]]; then
---
> if [ -w "$bash_config" ]; then
263c263
< echo -e '\n# bun'
---
> printf '\n# bun'
265,268c265,267
< for command in "${commands[@]}"; do
< echo "$command"
< done
< } >>"$bash_config"
---
> echo "$export_install_env"
> echo "$export_path"
> } >> "$bash_config"
276a276
> IFS="${my_ifs}"
278c278,279
< if [[ $set_manually = true ]]; then
---
> if [ "$set_manually" = true ]; then
> tilde_bash_config=$(tildify "$HOME/.bashrc")
281,283c282,283
< for command in "${commands[@]}"; do
< info_bold " $command"
< done
---
> info_bold " $export_install_env"
> info_bold " $export_path"
297c297
< if [[ $refresh_command ]]; then
---
> if [ -n "$refresh_command" ]; then
Thanks so much for tinkering on this! Indeed we need to make the install script work on more platforms. Appreciate the patch, will take a look at getting this merged soon.
@Electroid there are a few parts with bashisms that I didn't understand (and due to syntax are hard to search for).
I put TODO on those. If there's any question about how to translate that more correctly I'm happy to help.
FWIW, :
is a POSIX-specificed alternative way of saying true
(the || :
phrase is just "possibly erroneous thing or true"). So the current translation provided by @coolaj86 should be fine. https://stackoverflow.com/questions/3224878/what-is-the-purpose-of-the-colon-gnu-bash-builtin
is there a reason this proof of concept script was never opened as a PR?
@paperdave I never got back around to it. Life. 🤷♂️
It's too bad GitHub doesn't have a "Weekend TODOs" list to pick from when I'm burnt out from regular work and need some fun time.
I'd be happy to do this next time I have some downtime to dedicate to it.
What is the problem this feature would solve?
Bun would be universally installable on any POSIX system for which a binary exists.
Well... actually would need a POSIX .tar as well, but... one thing at a time. :) \ (and although
.gz
isn't POSIX, it seems to be more widely adopted than POSIX.Z
)Context: I'm adding
bun
to Webi (https://beta.webinstall.dev/bun). We do POSIX because over the years enough people have complained about bash scripts breaking stuff.What is the feature you are proposing to solve the problem?
Running
shellcheck
andshfmt
in POSIX mode (i.e. change the shebange to#!/bin/sh
oninstall.sh
What alternatives have you considered?
N/A
P.S. I'm drafting this up right now. I don't know where install.sh lives, but I'll post below in probably 10 minutes or so.
P.P.S. Taking a little longer than I thought - there's A LOT of fairly complex bashisms here.
P.P.P.S. There's very inconsistent use of
$x
vs${x}
vs"$x"
vs"${x}"
, so I'm curious to whether or not directories with spaces in their names are currently allowed.Where is install.sh?
I didn't see a repo that seemed like a good place to do a PR.
PR Process
Trying to look at all of these changes in aggregate is pretty much impossible, but I can break them down into feature-by-feature commits such that each checkmarked change is very easy to digest and understand its correctness.
(this, of course, is just a Works On My Machine™ Proof-of-Concept)