Files
bun.sh/scripts/bootstrap.sh
2025-05-29 16:07:35 -07:00

1364 lines
28 KiB
Bash
Executable File

#!/bin/sh
# Version: 11
# A script that installs the dependencies needed to build and test Bun.
# This should work on macOS and Linux with a POSIX shell.
# If this script does not work on your machine, please open an issue:
# https://github.com/oven-sh/bun/issues
# If you need to make a change to this script, such as upgrading a dependency,
# increment the version comment to indicate that a new image should be built.
# Otherwise, the existing image will be retroactively updated.
pid="$$"
print() {
echo "$@"
}
error() {
print "error: $@" >&2
if ! [ "$$" = "$pid" ]; then
kill -s TERM "$pid"
fi
exit 1
}
execute() {
print "$ $@" >&2
if ! "$@"; then
error "Command failed: $@"
fi
}
execute_sudo() {
if [ "$sudo" = "1" ] || [ -z "$can_sudo" ]; then
execute "$@"
else
execute sudo -n "$@"
fi
}
execute_as_user() {
sh="$(require sh)"
if [ "$sudo" = "1" ] || [ "$can_sudo" = "1" ]; then
if [ -f "$(which sudo)" ]; then
execute sudo -n -u "$user" "$sh" -lc "$*"
elif [ -f "$(which doas)" ]; then
execute doas -u "$user" "$sh" -lc "$*"
elif [ -f "$(which su)" ]; then
execute su -s "$sh" "$user" -lc "$*"
else
execute "$sh" -lc "$*"
fi
else
execute "$sh" -lc "$*"
fi
}
grant_to_user() {
path="$1"
if ! [ -f "$path" ] && ! [ -d "$path" ]; then
error "Could not find file or directory: \"$path\""
fi
chown="$(require chown)"
execute_sudo "$chown" -R "$user:$group" "$path"
execute_sudo chmod -R 777 "$path"
}
which() {
command -v "$1"
}
require() {
path="$(which "$1")"
if ! [ -f "$path" ]; then
error "Command \"$1\" is required, but is not installed."
fi
print "$path"
}
fetch() {
curl="$(which curl)"
if [ -f "$curl" ]; then
execute "$curl" -fsSL "$1"
else
wget="$(which wget)"
if [ -f "$wget" ]; then
execute "$wget" -qO- "$1"
else
error "Command \"curl\" or \"wget\" is required, but is not installed."
fi
fi
}
compare_version() {
if [ "$1" = "$2" ]; then
print "0"
elif [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]; then
print "-1"
else
print "1"
fi
}
create_directory() {
path="$1"
path_dir="$path"
while ! [ -d "$path_dir" ]; do
path_dir="$(dirname "$path_dir")"
done
path_needs_sudo="0"
if ! [ -r "$path_dir" ] || ! [ -w "$path_dir" ]; then
path_needs_sudo="1"
fi
mkdir="$(require mkdir)"
if [ "$path_needs_sudo" = "1" ]; then
execute_sudo "$mkdir" -p "$path"
else
execute "$mkdir" -p "$path"
fi
grant_to_user "$path"
}
create_tmp_directory() {
mktemp="$(require mktemp)"
path="$(execute "$mktemp" -d)"
grant_to_user "$path"
print "$path"
}
create_file() {
path="$1"
path_dir="$(dirname "$path")"
if ! [ -d "$path_dir" ]; then
create_directory "$path_dir"
fi
path_needs_sudo="0"
if ! [ -r "$path" ] || ! [ -w "$path" ]; then
path_needs_sudo="1"
fi
if [ "$path_needs_sudo" = "1" ]; then
execute_sudo touch "$path"
else
execute touch "$path"
fi
content="$2"
if [ -n "$content" ]; then
append_file "$path" "$content"
fi
grant_to_user "$path"
}
append_file() {
path="$1"
if ! [ -f "$path" ]; then
create_file "$path"
fi
path_needs_sudo="0"
if ! [ -r "$path" ] || ! [ -w "$path" ]; then
path_needs_sudo="1"
fi
content="$2"
print "$content" | while read -r line; do
if ! grep -q "$line" "$path"; then
sh="$(require sh)"
if [ "$path_needs_sudo" = "1" ]; then
execute_sudo "$sh" -c "echo '$line' >> '$path'"
else
execute "$sh" -c "echo '$line' >> '$path'"
fi
fi
done
}
download_file() {
file_url="$1"
file_tmp_dir="$(create_tmp_directory)"
file_tmp_path="$file_tmp_dir/$(basename "$file_url")"
fetch "$file_url" >"$file_tmp_path"
grant_to_user "$file_tmp_path"
print "$file_tmp_path"
}
append_to_profile() {
content="$1"
profiles=".profile .zprofile .bash_profile .bashrc .zshrc"
for profile in $profiles; do
for profile_path in "$current_home/$profile" "$home/$profile"; do
if [ "$ci" = "1" ] || [ -f "$profile_path" ]; then
append_file "$profile_path" "$content"
fi
done
done
}
append_to_path() {
path="$1"
if ! [ -d "$path" ]; then
error "Could not find directory: \"$path\""
fi
append_to_profile "export PATH=\"$path:\$PATH\""
export PATH="$path:$PATH"
}
move_to_bin() {
exe_path="$1"
if ! [ -f "$exe_path" ]; then
error "Could not find executable: \"$exe_path\""
fi
usr_paths="/usr/bin /usr/local/bin"
for usr_path in $usr_paths; do
if [ -d "$usr_path" ] && [ -w "$usr_path" ]; then
break
fi
done
grant_to_user "$exe_path"
execute_sudo mv -f "$exe_path" "$usr_path/$(basename "$exe_path")"
}
check_features() {
print "Checking features..."
for arg in "$@"; do
case "$arg" in
*--ci*)
ci=1
print "CI: enabled"
;;
*--osxcross*)
osxcross=1
print "Cross-compiling to macOS: enabled"
;;
*--gcc-13*)
gcc_version="13"
print "GCC 13: enabled"
;;
esac
done
}
check_operating_system() {
print "Checking operating system..."
uname="$(require uname)"
os="$("$uname" -s)"
case "$os" in
Linux*)
os="linux"
;;
Darwin*)
os="darwin"
;;
*)
error "Unsupported operating system: $os"
;;
esac
print "Operating System: $os"
arch="$("$uname" -m)"
case "$arch" in
x86_64 | x64 | amd64)
arch="x64"
;;
aarch64 | arm64)
arch="aarch64"
;;
*)
error "Unsupported architecture: $arch"
;;
esac
print "Architecture: $arch"
kernel="$("$uname" -r)"
print "Kernel: $kernel"
case "$os" in
linux)
if [ -f "/etc/alpine-release" ]; then
distro="alpine"
abi="musl"
alpine="$(cat /etc/alpine-release)"
if [ "$alpine" ~ "_" ]; then
release="$(print "$alpine" | cut -d_ -f1)-edge"
else
release="$alpine"
fi
elif [ -f "/etc/os-release" ]; then
. /etc/os-release
if [ -n "$ID" ]; then
distro="$ID"
fi
if [ -n "$VERSION_ID" ]; then
release="$VERSION_ID"
fi
fi
;;
darwin)
sw_vers="$(which sw_vers)"
if [ -f "$sw_vers" ]; then
distro="$("$sw_vers" -productName)"
release="$("$sw_vers" -productVersion)"
fi
case "$arch" in
x64)
sysctl="$(which sysctl)"
if [ -f "$sysctl" ] && [ "$("$sysctl" -n sysctl.proc_translated 2>/dev/null)" = "1" ]; then
arch="aarch64"
rosetta="1"
print "Rosetta: enabled"
fi
;;
esac
;;
esac
if [ -n "$distro" ]; then
print "Distribution: $distro $release"
fi
case "$os" in
linux)
ldd="$(which ldd)"
if [ -f "$ldd" ]; then
ldd_version="$($ldd --version 2>&1)"
abi_version="$(print "$ldd_version" | grep -o -E '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n 1)"
case "$ldd_version" in
*musl*)
abi="musl"
;;
*GNU* | *GLIBC*)
abi="gnu"
;;
esac
fi
if [ -n "$abi" ]; then
print "ABI: $abi $abi_version"
fi
;;
esac
}
check_inside_docker() {
if ! [ "$os" = "linux" ]; then
return
fi
print "Checking if inside Docker..."
if [ -f "/.dockerenv" ]; then
docker=1
else
if [ -f "/proc/1/cgroup" ]; then
case "$(cat /proc/1/cgroup)" in
*/docker/*)
docker=1
;;
esac
fi
if [ -f "/proc/self/mountinfo" ]; then
case "$(cat /proc/self/mountinfo)" in
*/docker/*)
docker=1
;;
esac
fi
fi
if [ "$docker" = "1" ]; then
print "Docker: enabled"
fi
}
check_package_manager() {
print "Checking package manager..."
case "$os" in
darwin)
if ! [ -f "$(which brew)" ]; then
install_brew
fi
pm="brew"
;;
linux)
if [ -f "$(which apt)" ]; then
pm="apt"
elif [ -f "$(which dnf)" ]; then
pm="dnf"
elif [ -f "$(which yum)" ]; then
pm="yum"
elif [ -f "$(which apk)" ]; then
pm="apk"
else
error "No package manager found. (apt, dnf, yum, apk)"
fi
;;
esac
print "Package manager: $pm"
print "Updating package manager..."
case "$pm" in
apt)
export DEBIAN_FRONTEND=noninteractive
package_manager update -y
;;
apk)
package_manager update
;;
esac
}
check_user() {
print "Checking user..."
if [ -n "$SUDO_USER" ]; then
user="$SUDO_USER"
else
id="$(require id)"
user="$("$id" -un)"
group="$("$id" -gn)"
fi
if [ -z "$user" ]; then
error "Could not determine user"
fi
print "User: $user"
print "Group: $group"
home="$(execute_as_user echo '~')"
if [ -z "$home" ] || [ "$home" = "~" ]; then
error "Could not determine home directory for user: $user"
fi
print "Home: $home"
id="$(which id)"
if [ -f "$id" ] && [ "$($id -u)" = "0" ]; then
sudo=1
print "Sudo: enabled"
elif [ -f "$(which sudo)" ] && [ "$(sudo -n echo 1 2>/dev/null)" = "1" ]; then
can_sudo=1
print "Sudo: can be used"
fi
current_user="$user"
current_group="$group"
current_home="$home"
}
check_ulimit() {
if ! [ "$ci" = "1" ]; then
return
fi
print "Checking ulimits..."
systemd_conf="/etc/systemd/system.conf"
if [ -f "$systemd_conf" ]; then
limits_conf="/etc/security/limits.d/99-unlimited.conf"
create_file "$limits_conf"
fi
limits="core data fsize memlock nofile rss stack cpu nproc as locks sigpending msgqueue"
for limit in $limits; do
limit_upper="$(print "$limit" | tr '[:lower:]' '[:upper:]')"
limit_value="unlimited"
case "$limit" in
nofile | nproc)
limit_value="1048576"
;;
esac
if [ -f "$limits_conf" ]; then
limit_users="root *"
for limit_user in $limit_users; do
append_file "$limits_conf" "$limit_user soft $limit $limit_value"
append_file "$limits_conf" "$limit_user hard $limit $limit_value"
done
fi
if [ -f "$systemd_conf" ]; then
append_file "$systemd_conf" "DefaultLimit$limit_upper=$limit_value"
fi
done
rc_conf="/etc/rc.conf"
if [ -f "$rc_conf" ]; then
rc_ulimit=""
limit_flags="c d e f i l m n q r s t u v x"
for limit_flag in $limit_flags; do
limit_value="unlimited"
case "$limit_flag" in
n | u)
limit_value="1048576"
;;
esac
rc_ulimit="$rc_ulimit -$limit_flag $limit_value"
done
append_file "$rc_conf" "rc_ulimit=\"$rc_ulimit\""
fi
pam_confs="/etc/pam.d/common-session /etc/pam.d/common-session-noninteractive"
for pam_conf in $pam_confs; do
if [ -f "$pam_conf" ]; then
append_file "$pam_conf" "session optional pam_limits.so"
fi
done
systemctl="$(which systemctl)"
if [ -f "$systemctl" ]; then
execute_sudo "$systemctl" daemon-reload
fi
# Configure dpkg and apt for faster operation in CI environments
if [ "$ci" = "1" ] && [ "$pm" = "apt" ]; then
dpkg_conf="/etc/dpkg/dpkg.cfg.d/01-ci-options"
execute_sudo create_directory "$(dirname "$dpkg_conf")"
append_file "$dpkg_conf" "force-unsafe-io"
append_file "$dpkg_conf" "no-debsig"
apt_conf="/etc/apt/apt.conf.d/99-ci-options"
execute_sudo create_directory "$(dirname "$apt_conf")"
append_file "$apt_conf" 'Acquire::Languages "none";'
append_file "$apt_conf" 'Acquire::GzipIndexes "true";'
append_file "$apt_conf" 'Acquire::CompressionTypes::Order:: "gz";'
append_file "$apt_conf" 'APT::Get::Install-Recommends "false";'
append_file "$apt_conf" 'APT::Get::Install-Suggests "false";'
append_file "$apt_conf" 'Dpkg::Options { "--force-confdef"; "--force-confold"; }'
fi
}
package_manager() {
case "$pm" in
apt)
execute_sudo apt "$@"
;;
dnf)
case "$distro" in
rhel)
execute_sudo dnf \
--disableplugin=subscription-manager \
"$@"
;;
*)
execute_sudo dnf "$@"
;;
esac
;;
yum)
execute_sudo yum "$@"
;;
apk)
execute_sudo apk "$@"
;;
brew)
execute_as_user brew "$@"
;;
*)
error "Unsupported package manager: $pm"
;;
esac
}
check_package() {
case "$pm" in
apt)
apt-cache policy "$1"
;;
dnf | yum | brew)
package_manager info "$1"
;;
*)
error "Unsupported package manager: $pm"
;;
esac
}
install_packages() {
case "$pm" in
apt)
package_manager install \
--yes \
--no-install-recommends \
"$@"
;;
dnf)
package_manager install \
--assumeyes \
--nodocs \
--noautoremove \
--allowerasing \
"$@"
;;
yum)
package_manager install -y "$@"
;;
brew)
package_manager install \
--force \
--formula \
"$@"
package_manager link \
--force \
--overwrite \
"$@"
;;
apk)
package_manager add \
--no-cache \
--no-interactive \
--no-progress \
"$@"
;;
*)
error "Unsupported package manager: $pm"
;;
esac
}
install_brew() {
print "Installing Homebrew..."
bash="$(require bash)"
script=$(download_file "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh")
execute_as_user "$bash" -lc "NONINTERACTIVE=1 $script"
case "$arch" in
x64)
append_to_path "/usr/local/bin"
;;
aarch64)
append_to_path "/opt/homebrew/bin"
;;
esac
case "$ci" in
1)
append_to_profile "export HOMEBREW_NO_INSTALL_CLEANUP=1"
append_to_profile "export HOMEBREW_NO_AUTO_UPDATE=1"
append_to_profile "export HOMEBREW_NO_ANALYTICS=1"
;;
esac
}
install_common_software() {
case "$pm" in
apt)
install_packages \
apt-transport-https \
software-properties-common
;;
dnf)
install_packages \
dnf-plugins-core
;;
esac
case "$distro" in
amzn)
install_packages \
tar
;;
rhel)
rhel_version="$(execute rpm -E %rhel)"
install_packages \
"https://dl.fedoraproject.org/pub/epel/epel-release-latest-$rhel_version.noarch.rpm"
;;
centos)
install_packages \
epel-release
;;
esac
crb="$(which crb)"
if [ -f "$crb" ]; then
execute "$crb" enable
fi
install_packages \
bash \
ca-certificates \
curl \
htop \
gnupg \
git \
unzip \
wget
install_rosetta
install_nodejs
install_bun
install_tailscale
install_buildkite
}
nodejs_version_exact() {
# https://unofficial-builds.nodejs.org/download/release/
if ! [ "$abi" = "musl" ] && [ -n "$abi_version" ] && ! [ "$(compare_version "$abi_version" "2.27")" = "1" ]; then
print "16.9.1"
else
print "22.9.0"
fi
}
nodejs_version() {
print "$(nodejs_version_exact)" | cut -d. -f1
}
install_nodejs() {
case "$pm" in
dnf | yum)
bash="$(require bash)"
script=$(download_file "https://rpm.nodesource.com/setup_$(nodejs_version).x")
execute_sudo "$bash" "$script"
;;
apt)
bash="$(require bash)"
script="$(download_file "https://deb.nodesource.com/setup_$(nodejs_version).x")"
execute_sudo "$bash" "$script"
;;
esac
case "$pm" in
apk)
install_packages nodejs npm
;;
*)
install_packages nodejs
;;
esac
# Some distros do not install the node headers by default.
# These are needed for certain FFI tests, such as: `cc.test.ts`
case "$distro" in
alpine | amzn)
install_nodejs_headers
;;
esac
}
install_nodejs_headers() {
nodejs_headers_tar="$(download_file "https://nodejs.org/download/release/v$(nodejs_version_exact)/node-v$(nodejs_version_exact)-headers.tar.gz")"
nodejs_headers_dir="$(dirname "$nodejs_headers_tar")"
execute tar -xzf "$nodejs_headers_tar" -C "$nodejs_headers_dir"
nodejs_headers_include="$nodejs_headers_dir/node-v$(nodejs_version_exact)/include"
execute_sudo cp -R "$nodejs_headers_include/" "/usr"
}
bun_version_exact() {
print "1.2.0"
}
install_bun() {
install_packages unzip
case "$pm" in
apk)
install_packages \
libgcc \
libstdc++
;;
esac
case "$abi" in
musl)
bun_triplet="bun-$os-$arch-$abi"
;;
*)
bun_triplet="bun-$os-$arch"
;;
esac
unzip="$(require unzip)"
bun_download_url="https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/bun-v$(bun_version_exact)/$bun_triplet.zip"
bun_zip="$(download_file "$bun_download_url")"
bun_tmpdir="$(dirname "$bun_zip")"
execute "$unzip" -o "$bun_zip" -d "$bun_tmpdir"
move_to_bin "$bun_tmpdir/$bun_triplet/bun"
bun_path="$(require bun)"
execute_sudo ln -sf "$bun_path" "$(dirname "$bun_path")/bunx"
}
install_cmake() {
case "$os-$pm" in
darwin-* | linux-apk)
install_packages cmake
;;
linux-*)
sh="$(require sh)"
cmake_version="3.30.5"
case "$arch" in
x64)
cmake_url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-x86_64.sh"
;;
aarch64)
cmake_url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-aarch64.sh"
;;
esac
cmake_script=$(download_file "$cmake_url")
execute_sudo "$sh" "$cmake_script" \
--skip-license \
--prefix=/usr
;;
esac
}
install_rosetta() {
case "$os" in
darwin)
if ! [ "$(which arch)" ]; then
execute softwareupdate \
--install-rosetta \
--agree-to-license
fi
;;
esac
}
install_build_essentials() {
case "$pm" in
apt)
install_packages \
build-essential \
ninja-build \
xz-utils \
pkg-config \
golang
;;
dnf | yum)
install_packages \
gcc-c++ \
xz \
pkg-config \
golang
case "$distro" in
rhel) ;;
*)
install_packages ninja-build
;;
esac
;;
brew)
install_packages \
ninja \
pkg-config \
golang
;;
apk)
install_packages \
build-base \
linux-headers \
ninja \
go \
xz
;;
esac
case "$distro-$pm" in
amzn-dnf)
package_manager groupinstall -y "Development Tools"
;;
esac
install_packages \
make \
python3 \
libtool \
ruby \
perl
install_cmake
install_llvm
install_osxcross
install_gcc
install_ccache
install_rust
install_docker
}
llvm_version_exact() {
print "19.1.7"
}
llvm_version() {
print "$(llvm_version_exact)" | cut -d. -f1
}
install_llvm() {
case "$pm" in
apt)
bash="$(require bash)"
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
# Install llvm-symbolizer explicitly to ensure it's available for ASAN
install_packages "llvm-$(llvm_version)-tools"
;;
brew)
install_packages "llvm@$(llvm_version)"
;;
apk)
# alpine doesn't have a lld19 package on 3.21 atm so use bare one for now
install_packages \
"llvm$(llvm_version)" \
"clang$(llvm_version)" \
"scudo-malloc" \
"lld" \
"llvm$(llvm_version)-dev" # Ensures llvm-symbolizer is installed
;;
esac
}
install_gcc() {
if ! [ "$os" = "linux" ] || ! [ "$distro" = "ubuntu" ] || [ -z "$gcc_version" ]; then
return
fi
# Taken from WebKit's Dockerfile.
# https://github.com/oven-sh/WebKit/blob/816a3c02e0f8b53f8eec06b5ed911192589b51e2/Dockerfile
execute_sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
execute_sudo apt update -y
execute_sudo apt install -y \
"gcc-$gcc_version" \
"g++-$gcc_version" \
"libgcc-$gcc_version-dev" \
"libstdc++-$gcc_version-dev" \
libasan6 \
libubsan1 \
libatomic1 \
libtsan0 \
liblsan0 \
libgfortran5 \
libc6-dev
execute_sudo update-alternatives \
--install /usr/bin/gcc gcc "/usr/bin/gcc-$gcc_version" 130 \
--slave /usr/bin/g++ g++ "/usr/bin/g++-$gcc_version" \
--slave /usr/bin/gcc-ar gcc-ar "/usr/bin/gcc-ar-$gcc_version" \
--slave /usr/bin/gcc-nm gcc-nm "/usr/bin/gcc-nm-$gcc_version" \
--slave /usr/bin/gcc-ranlib gcc-ranlib "/usr/bin/gcc-ranlib-$gcc_version"
case "$arch" in
x64)
arch_path="x86_64-linux-gnu"
;;
aarch64)
arch_path="aarch64-linux-gnu"
;;
esac
llvm_v="19"
append_to_profile "export CC=clang-${llvm_v}"
append_to_profile "export CXX=clang++-${llvm_v}"
append_to_profile "export AR=llvm-ar-${llvm_v}"
append_to_profile "export RANLIB=llvm-ranlib-${llvm_v}"
append_to_profile "export LD=lld-${llvm_v}"
append_to_profile "export LD_LIBRARY_PATH=/usr/lib/gcc/${arch_path}/${gcc_version}:/usr/lib/${arch_path}"
append_to_profile "export LIBRARY_PATH=/usr/lib/gcc/${arch_path}/${gcc_version}:/usr/lib/${arch_path}"
append_to_profile "export CPLUS_INCLUDE_PATH=/usr/include/c++/${gcc_version}:/usr/include/${arch_path}/c++/${gcc_version}"
append_to_profile "export C_INCLUDE_PATH=/usr/lib/gcc/${arch_path}/${gcc_version}/include"
gcc_path="/usr/lib/gcc/$arch_path/$gcc_version"
create_directory "$gcc_path"
execute_sudo ln -sf /usr/lib/$arch_path/libstdc++.so.6 "$gcc_path/libstdc++.so.6"
ld_conf_path="/etc/ld.so.conf.d/gcc-$gcc_version.conf"
append_file "$ld_conf_path" "$gcc_path"
append_file "$ld_conf_path" "/usr/lib/$arch_path"
execute_sudo ldconfig
execute_sudo ln -sf $(which clang-$llvm_v) /usr/bin/clang
execute_sudo ln -sf $(which clang++-$llvm_v) /usr/bin/clang++
execute_sudo ln -sf $(which lld-$llvm_v) /usr/bin/lld
execute_sudo ln -sf $(which lldb-$llvm_v) /usr/bin/lldb
execute_sudo ln -sf $(which clangd-$llvm_v) /usr/bin/clangd
execute_sudo ln -sf $(which llvm-ar-$llvm_v) /usr/bin/llvm-ar
execute_sudo ln -sf $(which ld.lld-$llvm_v) /usr/bin/ld
execute_sudo ln -sf $(which clang) /usr/bin/cc
execute_sudo ln -sf $(which clang++) /usr/bin/c++
# Make sure llvm-symbolizer is available for ASAN
execute_sudo ln -sf $(which llvm-symbolizer-$llvm_v) /usr/bin/llvm-symbolizer
}
install_ccache() {
case "$pm" in
apt | apk | brew)
install_packages ccache
;;
esac
}
install_rust() {
case "$pm" in
apk)
install_packages \
rust \
cargo
;;
*)
rust_home="/opt/rust"
create_directory "$rust_home"
append_to_profile "export RUSTUP_HOME=$rust_home"
append_to_profile "export CARGO_HOME=$rust_home"
sh="$(require sh)"
rustup_script=$(download_file "https://sh.rustup.rs")
execute "$sh" -lc "$rustup_script -y --no-modify-path"
append_to_path "$rust_home/bin"
;;
esac
case "$osxcross" in
1)
rustup="$(require rustup)"
execute_as_user "$rustup" target add aarch64-apple-darwin
execute_as_user "$rustup" target add x86_64-apple-darwin
;;
esac
}
install_docker() {
case "$pm" in
brew)
if ! [ -d "/Applications/Docker.app" ]; then
package_manager install docker --cask
fi
;;
*)
case "$distro-$release" in
amzn-2 | amzn-1)
execute_sudo amazon-linux-extras install docker
;;
amzn-* | alpine-*)
install_packages docker
;;
*)
sh="$(require sh)"
script=$(download_file "https://get.docker.com")
execute "$sh" "$script"
;;
esac
;;
esac
systemctl="$(which systemctl)"
if [ -f "$systemctl" ]; then
execute_sudo "$systemctl" enable docker
fi
getent="$(which getent)"
if [ -n "$("$getent" group docker)" ]; then
usermod="$(which usermod)"
if [ -f "$usermod" ]; then
execute_sudo "$usermod" -aG docker "$user"
fi
fi
}
macos_sdk_version() {
# https://github.com/alexey-lysiuk/macos-sdk/releases
print "13.3"
}
install_osxcross() {
if ! [ "$os" = "linux" ] || ! [ "$osxcross" = "1" ]; then
return
fi
install_packages \
libssl-dev \
lzma-dev \
libxml2-dev \
zlib1g-dev \
bzip2 \
cpio
osxcross_path="/opt/osxcross"
create_directory "$osxcross_path"
osxcross_commit="29fe6dd35522073c9df5800f8cd1feb4b9a993a8"
osxcross_tar="$(download_file "https://github.com/tpoechtrager/osxcross/archive/$osxcross_commit.tar.gz")"
execute tar -xzf "$osxcross_tar" -C "$osxcross_path"
osxcross_build_path="$osxcross_path/build"
execute mv "$osxcross_path/osxcross-$osxcross_commit" "$osxcross_build_path"
osxcross_sdk_tar="$(download_file "https://github.com/alexey-lysiuk/macos-sdk/releases/download/$(macos_sdk_version)/MacOSX$(macos_sdk_version).tar.xz")"
execute mv "$osxcross_sdk_tar" "$osxcross_build_path/tarballs/MacOSX$(macos_sdk_version).sdk.tar.xz"
bash="$(require bash)"
execute_sudo ln -sf "$(which clang-$(llvm_version))" /usr/bin/clang
execute_sudo ln -sf "$(which clang++-$(llvm_version))" /usr/bin/clang++
execute_sudo "$bash" -lc "UNATTENDED=1 TARGET_DIR='$osxcross_path' $osxcross_build_path/build.sh"
execute_sudo rm -rf "$osxcross_build_path"
grant_to_user "$osxcross_path"
}
install_tailscale() {
if [ "$docker" = "1" ]; then
return
fi
case "$os" in
linux)
sh="$(require sh)"
tailscale_script=$(download_file "https://tailscale.com/install.sh")
execute "$sh" "$tailscale_script"
;;
darwin)
install_packages go
execute_as_user go install tailscale.com/cmd/tailscale{,d}@latest
append_to_path "$home/go/bin"
;;
esac
}
install_fuse_python() {
# only linux needs this
case "$pm" in
apk)
# Build and install from source (https://github.com/libfuse/python-fuse/blob/master/INSTALL)
install_packages \
python3-dev \
fuse-dev \
pkgconf \
py3-setuptools
python_fuse_version="1.0.9"
python_fuse_tarball=$(download_file "https://github.com/libfuse/python-fuse/archive/refs/tags/v$python_fuse_version.tar.gz")
python_fuse_tmpdir="$(dirname "$python_fuse_tarball")"
execute tar -xzf "$python_fuse_tarball" -C "$python_fuse_tmpdir"
execute sh -c "cd '$python_fuse_tmpdir/python-fuse-$python_fuse_version' && python setup.py build"
execute_sudo sh -c "cd '$python_fuse_tmpdir/python-fuse-$python_fuse_version' && python setup.py install"
# For Alpine we also need to make sure the kernel module is automatically loaded
execute_sudo sh -c "echo fuse >> /etc/modules-load.d/fuse.conf"
# Check that it was actually installed
execute python -c 'import fuse'
;;
apt | dnf | yum)
install_packages python3-fuse
;;
esac
}
create_buildkite_user() {
if ! [ "$ci" = "1" ] || ! [ "$os" = "linux" ]; then
return
fi
print "Creating Buildkite user..."
user="buildkite-agent"
group="$user"
home="/var/lib/buildkite-agent"
case "$distro" in
amzn)
install_packages \
shadow-utils \
util-linux
;;
esac
if [ -z "$(getent passwd "$user")" ]; then
case "$distro" in
alpine)
execute_sudo addgroup \
--system "$group"
execute_sudo adduser "$user" \
--system \
--ingroup "$group" \
--shell "$(require sh)" \
--home "$home" \
--disabled-password
;;
*)
execute_sudo useradd "$user" \
--system \
--shell "$(require sh)" \
--no-create-home \
--home-dir "$home"
;;
esac
fi
if [ -n "$(getent group docker)" ]; then
execute_sudo usermod -aG docker "$user"
fi
buildkite_paths="$home /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /var/run/buildkite-agent/buildkite-agent.sock"
for path in $buildkite_paths; do
create_directory "$path"
done
buildkite_files="/var/run/buildkite-agent/buildkite-agent.pid"
for file in $buildkite_files; do
create_file "$file"
done
}
install_buildkite() {
if ! [ "$ci" = "1" ]; then
return
fi
buildkite_version="3.87.0"
case "$arch" in
aarch64)
buildkite_arch="arm64"
;;
x64)
buildkite_arch="amd64"
;;
esac
buildkite_filename="buildkite-agent-$os-$buildkite_arch-$buildkite_version.tar.gz"
buildkite_url="https://github.com/buildkite/agent/releases/download/v$buildkite_version/$buildkite_filename"
buildkite_tar="$(download_file "$buildkite_url")"
buildkite_tmpdir="$(dirname "$buildkite_tar")"
execute tar -xzf "$buildkite_tar" -C "$buildkite_tmpdir"
move_to_bin "$buildkite_tmpdir/buildkite-agent"
}
install_chromium() {
# https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-doesnt-launch-on-linux
# https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-in-the-cloud
case "$pm" in
apk)
install_packages \
chromium \
nss \
freetype \
harfbuzz \
ttf-freefont
;;
apt)
install_packages \
fonts-liberation \
libatk-bridge2.0-0 \
libatk1.0-0 \
libc6 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgbm1 \
libgcc1 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libstdc++6 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxss1 \
libxtst6 \
xdg-utils
# Fixes issue in newer version of Ubuntu:
# Package 'libasound2' has no installation candidate
if [ "$(check_package "libasound2t64")" ]; then
install_packages libasound2t64
else
install_packages libasound2
fi
;;
dnf | yum)
install_packages \
alsa-lib \
atk \
cups-libs \
gtk3 \
ipa-gothic-fonts \
libXcomposite \
libXcursor \
libXdamage \
libXext \
libXi \
libXrandr \
libXScrnSaver \
libXtst \
pango \
xorg-x11-fonts-100dpi \
xorg-x11-fonts-75dpi \
xorg-x11-fonts-cyrillic \
xorg-x11-fonts-misc \
xorg-x11-fonts-Type1 \
xorg-x11-utils
;;
esac
case "$distro" in
amzn)
install_packages \
mesa-libgbm
;;
esac
}
clean_system() {
if ! [ "$ci" = "1" ]; then
return
fi
print "Cleaning system..."
tmp_paths="/tmp /var/tmp"
for path in $tmp_paths; do
execute_sudo rm -rf "$path"/*
done
}
main() {
check_features "$@"
check_operating_system
check_inside_docker
check_user
check_ulimit
check_package_manager
create_buildkite_user
install_common_software
install_build_essentials
install_chromium
install_fuse_python
clean_system
}
main "$@"