mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
### What does this PR do? ### How did you verify your code works? --------- Co-authored-by: Claude <noreply@anthropic.com>
1805 lines
39 KiB
Bash
Executable File
1805 lines
39 KiB
Bash
Executable File
#!/bin/sh
|
|
# Version: 24
|
|
|
|
# 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() {
|
|
local opts=$-
|
|
set -x
|
|
"$@"
|
|
{ local status=$?; set +x "$opts"; } 2> /dev/null
|
|
if [ "$status" -ne 0 ]; 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"
|
|
}
|
|
|
|
# path=$(download_and_verify_file URL sha256)
|
|
download_and_verify_file() {
|
|
file_url="$1"
|
|
hash="$2"
|
|
|
|
path=$(download_file "$file_url")
|
|
execute sh -c 'echo "'"$hash $path"'" | sha256sum -c -' >/dev/null 2>&1
|
|
|
|
print "$path"
|
|
}
|
|
|
|
append_to_profile() {
|
|
content="$1"
|
|
profiles=".profile .zprofile .bash_profile .bashrc .zshrc .cshrc"
|
|
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"
|
|
;;
|
|
FreeBSD)
|
|
os="freebsd"
|
|
;;
|
|
*)
|
|
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
|
|
;;
|
|
freebsd)
|
|
. /etc/os-release
|
|
distro="$ID"
|
|
release="$VERSION_ID"
|
|
;;
|
|
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-get)" ]; 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
|
|
;;
|
|
freebsd)
|
|
pm="pkg"
|
|
;;
|
|
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
|
|
;;
|
|
pkg)
|
|
# may need to switch betwen 'latest' and 'quarterly' depending on which repo www/chromium is in. check https://www.freshports.org/www/chromium/.
|
|
# mkdir -p /usr/local/etc/pkg/repos
|
|
# echo 'FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
|
|
export ASSUME_ALWAYS_YES=yes
|
|
package_manager update -f
|
|
;;
|
|
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 ! [ "$os" = "linux" ]; then
|
|
return
|
|
fi
|
|
if ! [ "$ci" = "1" ]; then
|
|
return
|
|
fi
|
|
|
|
print "Checking ulimits..."
|
|
systemd_conf="/etc/systemd/system.conf"
|
|
limits_conf="/etc/security/limits.d/99-unlimited.conf"
|
|
create_file "$limits_conf"
|
|
|
|
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
|
|
# in systemd's configuration you need to say "infinity" when you mean "unlimited"
|
|
if [ "$limit_value" = "unlimited" ]; then
|
|
limit_value="infinity"
|
|
fi
|
|
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-get "$@"
|
|
;;
|
|
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 "$@"
|
|
;;
|
|
pkg)
|
|
execute_sudo pkg "$@"
|
|
;;
|
|
*)
|
|
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 \
|
|
--fix-missing \
|
|
"$@"
|
|
;;
|
|
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 \
|
|
"$@"
|
|
;;
|
|
pkg)
|
|
package_manager install "$@"
|
|
;;
|
|
*)
|
|
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)
|
|
# software-properties-common is not available in Debian Trixie
|
|
if [ "$distro" = "debian" ] && [ "$release" = "13" ]; then
|
|
install_packages \
|
|
apt-transport-https
|
|
else
|
|
install_packages \
|
|
apt-transport-https \
|
|
software-properties-common
|
|
fi
|
|
# https://packages.debian.org
|
|
# https://packages.ubuntu.com
|
|
install_packages \
|
|
bash \
|
|
ca-certificates \
|
|
curl \
|
|
htop \
|
|
gnupg \
|
|
git \
|
|
unzip \
|
|
wget \
|
|
libc6-dbg
|
|
;;
|
|
dnf)
|
|
# https://packages.fedoraproject.org
|
|
install_packages \
|
|
bash \
|
|
ca-certificates \
|
|
curl \
|
|
htop \
|
|
gnupg \
|
|
git \
|
|
unzip \
|
|
wget \
|
|
dnf-plugins-core
|
|
;;
|
|
apk)
|
|
# https://pkgs.alpinelinux.org/packages
|
|
install_packages \
|
|
bash \
|
|
ca-certificates \
|
|
curl \
|
|
htop \
|
|
gnupg \
|
|
git \
|
|
unzip \
|
|
wget \
|
|
;;
|
|
pkg)
|
|
# https://www.freshports.org
|
|
install_packages \
|
|
shells/bash \
|
|
ftp/curl \
|
|
sysutils/htop \
|
|
security/gnupg \
|
|
devel/git \
|
|
archivers/unzip \
|
|
ftp/wget \
|
|
editors/vim \
|
|
sysutils/neofetch \
|
|
;;
|
|
esac
|
|
|
|
case "$distro" in
|
|
amzn | alpine)
|
|
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_rosetta
|
|
install_nodejs
|
|
install_bun
|
|
install_tailscale
|
|
install_buildkite
|
|
}
|
|
|
|
nodejs_version_exact() {
|
|
print "24.3.0"
|
|
}
|
|
|
|
nodejs_version() {
|
|
print "$(nodejs_version_exact)" | cut -d. -f1
|
|
}
|
|
|
|
install_nodejs() {
|
|
# Download Node.js directly from nodejs.org
|
|
nodejs_version="$(nodejs_version_exact)"
|
|
|
|
# Determine platform name for Node.js download
|
|
case "$os" in
|
|
darwin)
|
|
nodejs_platform="darwin"
|
|
;;
|
|
linux)
|
|
nodejs_platform="linux"
|
|
;;
|
|
freebsd)
|
|
nodejs_platform="freebsd"
|
|
;;
|
|
*)
|
|
error "Unsupported OS for Node.js download: $os"
|
|
;;
|
|
esac
|
|
|
|
# Determine architecture name for Node.js download
|
|
case "$arch" in
|
|
x64)
|
|
nodejs_arch="x64"
|
|
;;
|
|
aarch64)
|
|
nodejs_arch="arm64"
|
|
;;
|
|
*)
|
|
error "Unsupported architecture for Node.js download: $arch"
|
|
;;
|
|
esac
|
|
|
|
if [ "$os" = "freebsd" ]; then
|
|
# TODO: use nodejs_version_exact
|
|
install_packages "www/node$(nodejs_version)" "www/npm-node$(nodejs_version)"
|
|
return
|
|
fi
|
|
|
|
case "$abi" in
|
|
musl)
|
|
nodejs_mirror="https://bun-nodejs-release.s3.us-west-1.amazonaws.com"
|
|
nodejs_foldername="node-v$nodejs_version-$nodejs_platform-$nodejs_arch-musl"
|
|
;;
|
|
*)
|
|
nodejs_mirror="https://nodejs.org/dist"
|
|
nodejs_foldername="node-v$nodejs_version-$nodejs_platform-$nodejs_arch"
|
|
;;
|
|
esac
|
|
|
|
# Download Node.js binary archive
|
|
nodejs_url="$nodejs_mirror/v$nodejs_version/$nodejs_foldername.tar.gz"
|
|
nodejs_tar="$(download_file "$nodejs_url")"
|
|
nodejs_extract_dir="$(dirname "$nodejs_tar")"
|
|
|
|
# Extract Node.js
|
|
execute tar -xzf "$nodejs_tar" -C "$nodejs_extract_dir"
|
|
|
|
# Install Node.js binaries to system
|
|
nodejs_dir="$nodejs_extract_dir/$nodejs_foldername"
|
|
|
|
# Copy bin files preserving symlinks
|
|
for file in "$nodejs_dir/bin/"*; do
|
|
filename="$(basename "$file")"
|
|
if [ -L "$file" ]; then
|
|
# Get the symlink target
|
|
target="$(readlink "$file")"
|
|
# The symlinks are relative (like ../lib/node_modules/npm/bin/npm-cli.js)
|
|
# and will work correctly from /usr/local/bin since we're copying
|
|
# node_modules to /usr/local/lib/node_modules
|
|
execute_sudo ln -sf "$target" "/usr/local/bin/$filename"
|
|
elif [ -f "$file" ]; then
|
|
# Copy regular files
|
|
execute_sudo cp -f "$file" "/usr/local/bin/$filename"
|
|
execute_sudo chmod +x "/usr/local/bin/$filename"
|
|
fi
|
|
done
|
|
|
|
# Copy node_modules directory to lib
|
|
if [ -d "$nodejs_dir/lib/node_modules" ]; then
|
|
execute_sudo mkdir -p "/usr/local/lib"
|
|
execute_sudo cp -Rf "$nodejs_dir/lib/node_modules" "/usr/local/lib/"
|
|
fi
|
|
|
|
# Copy include files if they exist
|
|
if [ -d "$nodejs_dir/include" ]; then
|
|
execute_sudo mkdir -p "/usr/local/include"
|
|
execute_sudo cp -Rf "$nodejs_dir/include/node" "/usr/local/include/"
|
|
fi
|
|
|
|
# Copy share files if they exist (man pages, etc.)
|
|
if [ -d "$nodejs_dir/share" ]; then
|
|
execute_sudo mkdir -p "/usr/local/share"
|
|
# Copy only node-specific directories
|
|
for sharedir in "$nodejs_dir/share/"*; do
|
|
if [ -d "$sharedir" ]; then
|
|
dirname="$(basename "$sharedir")"
|
|
execute_sudo cp -Rf "$sharedir" "/usr/local/share/"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Ensure /usr/local/bin is in PATH
|
|
if ! echo "$PATH" | grep -q "/usr/local/bin"; then
|
|
print "Adding /usr/local/bin to PATH"
|
|
append_to_profile 'export PATH="/usr/local/bin:$PATH"'
|
|
export PATH="/usr/local/bin:$PATH"
|
|
fi
|
|
|
|
# Verify Node.js installation
|
|
if ! command -v node >/dev/null 2>&1; then
|
|
error "Node.js installation failed: 'node' command not found in PATH"
|
|
fi
|
|
|
|
installed_version="$(node --version 2>/dev/null || echo "unknown")"
|
|
expected_version="v$nodejs_version"
|
|
|
|
if [ "$installed_version" != "$expected_version" ]; then
|
|
error "Node.js installation failed: expected version $expected_version but got $installed_version. Please check your PATH and try running 'which node' to debug."
|
|
fi
|
|
|
|
print "Node.js $installed_version installed successfully"
|
|
|
|
# Ensure that Node.js headers are always pre-downloaded so that we don't rely on node-gyp
|
|
install_nodejs_headers
|
|
}
|
|
|
|
install_nodejs_headers() {
|
|
if [ "$os" = "freebsd" ]; then
|
|
return
|
|
fi
|
|
|
|
nodejs_version="$(nodejs_version_exact)"
|
|
nodejs_headers_tar="$(download_file "https://nodejs.org/download/release/v$nodejs_version/node-v$nodejs_version-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/include"
|
|
execute_sudo cp -R "$nodejs_headers_include" "/usr/local/"
|
|
|
|
# Also install to node-gyp cache locations for different node-gyp versions
|
|
# This ensures node-gyp finds headers without downloading them
|
|
setup_node_gyp_cache "$nodejs_version" "$nodejs_headers_dir/node-v$nodejs_version"
|
|
}
|
|
|
|
setup_node_gyp_cache() {
|
|
if [ "$os" = "freebsd" ]; then
|
|
return
|
|
fi
|
|
|
|
nodejs_version="$1"
|
|
headers_source="$2"
|
|
|
|
cache_dir="$home/.cache/node-gyp/$nodejs_version"
|
|
|
|
create_directory "$cache_dir"
|
|
|
|
# Copy headers
|
|
if [ -d "$headers_source/include" ]; then
|
|
cp -R "$headers_source/include" "$cache_dir/" 2>/dev/null || true
|
|
fi
|
|
|
|
# Create installVersion file (node-gyp expects this)
|
|
echo "11" > "$cache_dir/installVersion" 2>/dev/null || true
|
|
|
|
# For Linux, we don't need .lib files like Windows
|
|
# but create the directory structure node-gyp expects
|
|
case "$arch" in
|
|
x86_64|amd64)
|
|
create_directory "$cache_dir/lib/x64" 2>/dev/null || true
|
|
;;
|
|
aarch64|arm64)
|
|
create_directory "$cache_dir/lib/arm64" 2>/dev/null || true
|
|
;;
|
|
*)
|
|
create_directory "$cache_dir/lib" 2>/dev/null || true
|
|
;;
|
|
esac
|
|
|
|
# Ensure entire path is accessible, not just last component
|
|
grant_to_user "$home/.cache"
|
|
}
|
|
|
|
bun_version_exact() {
|
|
print "1.3.1"
|
|
}
|
|
|
|
install_bun() {
|
|
if [ "$os" = "freebsd" ]; then
|
|
# TODO: need to complete bun bootstrap for for this work
|
|
return
|
|
fi
|
|
|
|
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
|
|
;;
|
|
freebsd-pkg)
|
|
install_packages devel/cmake
|
|
;;
|
|
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
|
|
install_packages apache2-utils
|
|
;;
|
|
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
|
|
install_packages apache2-utils
|
|
;;
|
|
esac
|
|
|
|
case "$distro-$pm" in
|
|
amzn-dnf)
|
|
package_manager groupinstall -y "Development Tools"
|
|
;;
|
|
esac
|
|
|
|
case "$os" in
|
|
linux)
|
|
install_packages \
|
|
make \
|
|
python3 \
|
|
libtool \
|
|
ruby \
|
|
perl \
|
|
;;
|
|
freebsd)
|
|
install_packages \
|
|
devel/ninja \
|
|
devel/pkgconf \
|
|
lang/go \
|
|
devel/gmake \
|
|
lang/python3 \
|
|
devel/libtool \
|
|
lang/ruby33 \
|
|
perl5 \
|
|
;;
|
|
esac
|
|
|
|
install_cmake
|
|
install_llvm
|
|
install_osxcross
|
|
install_gcc
|
|
install_rust
|
|
install_sccache
|
|
install_docker
|
|
}
|
|
|
|
llvm_version_exact() {
|
|
print "19.1.7"
|
|
}
|
|
|
|
llvm_version() {
|
|
print "$(llvm_version_exact)" | cut -d. -f1
|
|
}
|
|
|
|
install_llvm() {
|
|
case "$pm" in
|
|
apt)
|
|
# Debian 13 (Trixie) has LLVM 19 natively, and apt.llvm.org doesn't have a trixie repo
|
|
if [ "$distro" = "debian" ]; then
|
|
install_packages \
|
|
"llvm-$(llvm_version)" \
|
|
"clang-$(llvm_version)" \
|
|
"lld-$(llvm_version)" \
|
|
"llvm-$(llvm_version)-dev" \
|
|
"llvm-$(llvm_version)-tools"
|
|
else
|
|
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"
|
|
fi
|
|
;;
|
|
brew)
|
|
install_packages "llvm@$(llvm_version)"
|
|
;;
|
|
apk)
|
|
install_packages \
|
|
"llvm$(llvm_version)" \
|
|
"clang$(llvm_version)" \
|
|
"scudo-malloc" \
|
|
"lld$(llvm_version)" \
|
|
"llvm$(llvm_version)-dev" # Ensures llvm-symbolizer is installed
|
|
;;
|
|
esac
|
|
|
|
case "$os" in
|
|
freebsd)
|
|
# TODO: use llvm_version_exact
|
|
install_packages "devel/llvm$(llvm_version)"
|
|
;;
|
|
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_sccache() {
|
|
case "$os" in
|
|
linux)
|
|
;;
|
|
freebsd)
|
|
cargo install sccache --locked --version 0.12.0
|
|
return
|
|
;;
|
|
*)
|
|
error "Unsupported platform: $os"
|
|
;;
|
|
esac
|
|
|
|
# Alright, look, this function is cobbled together but it's only as cobbled
|
|
# together as this whole script is.
|
|
#
|
|
# For some reason, move_to_bin doesn't work here due to permissions so I'm
|
|
# avoiding that function. It's also wrong with permissions and so on.
|
|
#
|
|
# Unfortunately, we cannot use install_packages since many package managers
|
|
# don't compile `sccache` with S3 support.
|
|
local opts=$-
|
|
set -ef
|
|
|
|
local sccache_http
|
|
sccache_http="https://github.com/mozilla/sccache/releases/download/v0.12.0/sccache-v0.12.0-$(uname -m)-unknown-linux-musl.tar.gz"
|
|
|
|
local file
|
|
file=$(download_file "$sccache_http")
|
|
|
|
local tmpdir
|
|
tmpdir=$(mktemp -d)
|
|
|
|
execute tar -xzf "$file" -C "$tmpdir"
|
|
execute_sudo install -m755 "$tmpdir/sccache-v0.12.0-$(uname -m)-unknown-linux-musl/sccache" "/usr/local/bin"
|
|
|
|
set +ef -"$opts"
|
|
}
|
|
|
|
install_rust() {
|
|
case "$distro" in
|
|
freebsd)
|
|
install_packages lang/rust
|
|
create_directory "$HOME/.cargo/bin"
|
|
append_to_path "$HOME/.cargo/bin"
|
|
;;
|
|
*)
|
|
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"
|
|
|
|
# Ensure all rustup files are accessible (for CI builds where different users run builds)
|
|
grant_to_user "$rust_home"
|
|
;;
|
|
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
|
|
;;
|
|
pkg)
|
|
install_packages \
|
|
sysutils/docker \
|
|
sysutils/docker-compose \
|
|
;;
|
|
*)
|
|
case "$distro-$release" in
|
|
amzn-2 | amzn-1)
|
|
execute_sudo amazon-linux-extras install docker
|
|
;;
|
|
amzn-* | alpine-*)
|
|
install_packages docker docker-cli-compose
|
|
;;
|
|
*)
|
|
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
|
|
if [ "$os" = "linux" ] && [ "$distro" = "alpine" ]; then
|
|
execute doas rc-update add docker default
|
|
execute doas rc-service docker start
|
|
fi
|
|
|
|
getent="$(which getent)"
|
|
if [ -n "$("$getent" group docker)" ]; then
|
|
usermod="$(which usermod)"
|
|
if [ -z "$usermod" ]; then
|
|
usermod="$(sudo which usermod)"
|
|
fi
|
|
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"
|
|
;;
|
|
freebsd)
|
|
install_packages security/tailscale
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_fuse_python() {
|
|
if ! [ "$os" = "linux" ]; then
|
|
return
|
|
fi
|
|
|
|
# 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" ]; 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
|
|
;;
|
|
freebsd)
|
|
execute_sudo pw group add -n "$group"
|
|
execute_sudo pw user add \
|
|
-n "$user" \
|
|
-g "$group" \
|
|
-s "$(require sh)" \
|
|
-d "$home" \
|
|
;;
|
|
*)
|
|
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
|
|
|
|
# The following is necessary to configure buildkite to use a stable
|
|
# checkout directory. sccache hashes absolute paths into its cache keys,
|
|
# so if buildkite uses a different checkout path each time (which it does
|
|
# by default), sccache will be useless.
|
|
local opts=$-
|
|
set -ef
|
|
|
|
# I do not want to use create_file because it creates directories with 777
|
|
# permissions and files with 664 permissions. This is dumb, for obvious
|
|
# reasons.
|
|
local hook_dir=${home}/hooks
|
|
mkdir -p -m 755 "${hook_dir}"
|
|
cat << EOF > "${hook_dir}/environment"
|
|
#!/bin/sh
|
|
set -efu
|
|
|
|
export BUILDKITE_BUILD_CHECKOUT_PATH=${home}/build
|
|
EOF
|
|
execute_sudo chmod +x "${hook_dir}/environment"
|
|
execute_sudo chown -R "$user:$group" "$hook_dir"
|
|
|
|
set +ef -"$opts"
|
|
}
|
|
|
|
install_buildkite() {
|
|
if ! [ "$ci" = "1" ]; then
|
|
return
|
|
fi
|
|
|
|
buildkite_version="3.114.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
|
|
;;
|
|
pkg)
|
|
install_packages \
|
|
www/chromium \
|
|
;;
|
|
esac
|
|
|
|
case "$distro" in
|
|
amzn)
|
|
install_packages \
|
|
mesa-libgbm
|
|
;;
|
|
esac
|
|
}
|
|
|
|
install_age() {
|
|
age_version="1.2.1"
|
|
case "$os" in
|
|
linux)
|
|
case "$arch" in
|
|
x64)
|
|
age_arch="amd64"
|
|
age_hash="7df45a6cc87d4da11cc03a539a7470c15b1041ab2b396af088fe9990f7c79d50"
|
|
;;
|
|
aarch64)
|
|
age_arch="arm64"
|
|
age_hash="57fd79a7ece5fe501f351b9dd51a82fbee1ea8db65a8839db17f5c080245e99f"
|
|
;;
|
|
*)
|
|
error "Unsupported platform: $os-$arch"
|
|
;;
|
|
esac
|
|
;;
|
|
freebsd)
|
|
case "$arch" in
|
|
x64)
|
|
age_arch="amd64"
|
|
age_hash="943a7510a9973a1e589b913a70228aa1361a63cde39e3ed581435a4d4802df29"
|
|
;;
|
|
*)
|
|
error "Unsupported platform: $os-$arch"
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
error "Unsupported platform: $os-$arch"
|
|
;;
|
|
esac
|
|
|
|
age_tarball="$(download_and_verify_file https://github.com/FiloSottile/age/releases/download/v$age_version/age-v$age_version-$os-$age_arch.tar.gz "$age_hash")"
|
|
age_extract_dir="$(create_tmp_directory)"
|
|
execute tar -C "$age_extract_dir" -zxf "$age_tarball" age/age
|
|
move_to_bin "$age_extract_dir/age/age"
|
|
}
|
|
|
|
configure_core_dumps() {
|
|
case "$os" in
|
|
linux)
|
|
# set up a directory that the test runner will look in after running tests
|
|
cores_dir="/var/bun-cores-$distro-$release-$arch"
|
|
sysctl_file="/etc/sysctl.d/local.conf"
|
|
create_directory "$cores_dir"
|
|
# ensure core_pattern will point there
|
|
# %e = executable filename
|
|
# %p = pid
|
|
append_file "$sysctl_file" "kernel.core_pattern = $cores_dir/%e-%p.core"
|
|
|
|
# disable apport.service if it exists since it will override the core_pattern
|
|
if which systemctl >/dev/null; then
|
|
if systemctl list-unit-files apport.service >/dev/null; then
|
|
execute_sudo "$systemctl" disable --now apport.service
|
|
fi
|
|
fi
|
|
|
|
# load the new configuration (ignore permission errors)
|
|
execute_sudo sysctl -p "$sysctl_file"
|
|
|
|
# ensure that a regular user will be able to run sysctl
|
|
if [ -d /sbin ]; then
|
|
append_to_path /sbin
|
|
fi
|
|
|
|
# install gdb for backtraces
|
|
install_packages gdb
|
|
;;
|
|
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
|
|
}
|
|
|
|
ensure_no_tmpfs() {
|
|
if ! [ "$os" = "linux" ]; then
|
|
return
|
|
fi
|
|
if ! ( [ "$distro" = "ubuntu" ] || [ "$distro" = "debian" ] ); then
|
|
return
|
|
fi
|
|
|
|
execute_sudo systemctl mask tmp.mount
|
|
}
|
|
|
|
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
|
|
install_age
|
|
if [ "${BUN_NO_CORE_DUMP:-0}" != "1" ]; then
|
|
configure_core_dumps
|
|
fi
|
|
clean_system
|
|
ensure_no_tmpfs
|
|
}
|
|
|
|
main "$@"
|