Skip to content

Commit 3c73e92

Browse files
authored
Bootstrap: environmental sanitisation, package install robustness (#26433)
* Sanitise more. * Let's brute-force Gentoo prep. * Quiet, Gentoo. * Intel macOS, `brew` unsupported. * Intel macOS, `brew` unsupported. * Throttle. * Apparently there's a free `macos-26-intel` runner. * More safety. * More safety.
1 parent 4e67c28 commit 3c73e92

2 files changed

Lines changed: 103 additions & 21 deletions

File tree

.github/workflows/bootstrap_testing.yml

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,32 +83,34 @@ jobs:
8383
- name: Install base dependencies
8484
run: |
8585
# Attempt to run the package installation up to 10 times to mitigate transient network issues
86+
ok=''
8687
for n in $(seq 1 10); do
8788
{
8889
echo "Attempt #$n of 10 to install base dependencies:"
8990
case "${{ matrix.distribution }}" in
9091
*ubuntu*|*debian*)
91-
apt-get update
92-
apt-get install -y sudo git passwd
92+
apt-get update &&
93+
apt-get install -y sudo git passwd
9394
;;
9495
*fedora*|*rockylinux*|*almalinux*)
9596
dnf install -y sudo git passwd findutils # findutils=xargs
9697
;;
9798
*suse*)
98-
zypper --non-interactive refresh
99-
zypper --non-interactive install sudo git shadow findutils tar # findutils=xargs
99+
zypper --non-interactive refresh &&
100+
zypper --non-interactive install sudo git shadow findutils tar # findutils=xargs
100101
;;
101102
*gentoo*)
102-
emaint sync
103-
emerge --noreplace --ask=n sudo dev-vcs/git shadow findutils # findutils=xargs
103+
{ emaint sync -a || emerge --sync --quiet || emerge-webrsync -q ; } &&
104+
emerge --noreplace --ask=n sudo dev-vcs/git shadow findutils # findutils=xargs
104105
;;
105106
*archlinux*|*cachyos*|*manjaro*)
106-
pacman -Syu --noconfirm
107-
pacman -S --noconfirm sudo git
107+
pacman -Syu --noconfirm &&
108+
pacman -S --noconfirm sudo git
108109
;;
109110
esac
110-
} && break || sleep 10
111+
} && { ok=1; break ; } || sleep 10
111112
done
113+
[ -n "$ok" ] || exit 1
112114
113115
# Fix PAM configuration for sudo in containers
114116
# Fix /etc/shadow permissions - common issue in container environments
@@ -196,10 +198,21 @@ jobs:
196198
- macos-15 # Apple Silicon ARM64
197199
- macos-15-intel # Intel x64
198200
- macos-26 # Apple Silicon ARM64
201+
- macos-26-intel # Intel x64
199202

200203
runs-on: ${{ matrix.os }}
201204

202205
steps:
206+
- name: Install base dependencies
207+
run: |
208+
# Attempt to run the brew update up to 10 times to mitigate transient issues
209+
for n in $(seq 1 10); do
210+
brew update && exit 0
211+
brew update-reset || true
212+
sleep 10
213+
done
214+
exit 1
215+
203216
- name: Checkout repository
204217
uses: actions/checkout@v7
205218
with:

util/env-bootstrap.sh

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,49 @@
4343
# corrupt captured output and break pattern matching throughout this script.
4444
unset GREP_OPTIONS GREP_COLOR GREP_COLORS
4545

46+
# Force the C locale so `tr`/`grep`/`sed`/`sort` behave the same on every
47+
# system, and clear other variables which alter tool behavior.
48+
export LC_ALL=C
49+
unset CDPATH POSIXLY_CORRECT TAR_OPTIONS
50+
51+
# Drop out of any inherited Python venv; `deactivate` doesn't exist in this
52+
# process, so strip its $PATH entries and marker variables by hand.
53+
if [ -n "${VIRTUAL_ENV:-}" ]; then
54+
clean_path=''
55+
old_ifs="${IFS}"
56+
IFS=':'
57+
for path_entry in $PATH; do
58+
case "$path_entry" in
59+
"$VIRTUAL_ENV"/bin | "$VIRTUAL_ENV"/Scripts) ;;
60+
*) clean_path="${clean_path:+${clean_path}:}${path_entry}" ;;
61+
esac
62+
done
63+
IFS="${old_ifs}"
64+
PATH="${clean_path}"
65+
export PATH
66+
unset VIRTUAL_ENV VIRTUAL_ENV_PROMPT clean_path old_ifs path_entry
67+
fi
68+
69+
# Wipe all PYTHON* variables (keeping PYTHON_TARGET_VERSION, which this
70+
# script uses) so the user's Python settings can't leak into the
71+
# interpreters and builds managed by `uv`.
72+
saved_python_target_version="${PYTHON_TARGET_VERSION:-}"
73+
for env_var_name in $(env | LC_ALL=C sed -n 's/^\(PYTHON[A-Za-z0-9_]*\)=.*/\1/p'); do
74+
unset "$env_var_name" 2>/dev/null || true
75+
done
76+
[ -z "$saved_python_target_version" ] || export PYTHON_TARGET_VERSION="$saved_python_target_version"
77+
unset saved_python_target_version env_var_name
78+
export PYTHONNOUSERSITE=1
79+
80+
# Clear conda/pip/uv overrides which would affect dependency resolution or
81+
# builds; proxy, TLS, and index/mirror variables stay for corporate networks.
82+
unset CONDA_PREFIX CONDA_DEFAULT_ENV
83+
unset PIP_REQUIRE_VIRTUALENV PIP_TARGET PIP_PREFIX PIP_USER
84+
unset SETUPTOOLS_USE_DISTUTILS
85+
unset UV_NO_BUILD_ISOLATION UV_OFFLINE UV_NO_INDEX \
86+
UV_CONSTRAINT UV_BUILD_CONSTRAINT UV_OVERRIDE \
87+
UV_SYSTEM_PYTHON UV_NO_MANAGED_PYTHON
88+
4689
BOOTSTRAP_TMPDIR="$(mktemp -d /tmp/qmk-bootstrap-failure.XXXXXX)"
4790
trap 'rm -rf "$BOOTSTRAP_TMPDIR" >/dev/null 2>&1 || true' EXIT
4891
FAILURE_FILE="${BOOTSTRAP_TMPDIR}/fail"
@@ -175,6 +218,15 @@ __EOT__
175218
fi
176219
}
177220

221+
check_release_tag() {
222+
# An empty tag means the GitHub API call failed, usually from rate limiting.
223+
if [ -z "$2" ]; then
224+
echo "Could not determine the latest $1 release." >&2
225+
echo "If GitHub API rate limits are the cause, set GITHUB_TOKEN to raise them." >&2
226+
exit 1
227+
fi
228+
}
229+
178230
fn_os() {
179231
local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z')
180232
if [ -z "$os_name" ]; then
@@ -285,6 +337,10 @@ __EOT__
285337
print_package_manager_deps_and_delay() {
286338
get_package_manager_deps | tr ' ' '\n' | sort | xargs -I'{}' echo " - {}" >&2
287339
exit_if_execution_failed
340+
if [ -n "${1:-}" ]; then
341+
echo >&2
342+
echo "$1" >&2
343+
fi
288344
preinstall_delay || exit 1
289345
}
290346

@@ -294,7 +350,11 @@ __EOT__
294350
macos)
295351
if [ -n "$(command -v brew 2>/dev/null || true)" ]; then
296352
echo "It will also install the following system packages using 'brew':" >&2
297-
print_package_manager_deps_and_delay
353+
local intel_note=""
354+
if [ "$(fn_arch)" = "X64" ]; then
355+
intel_note="NOTE: Homebrew no longer provides pre-built packages for Intel Macs, so some of the above may be built from source. This can take a long time."
356+
fi
357+
print_package_manager_deps_and_delay "$intel_note"
298358

299359
brew update
300360

@@ -308,12 +368,14 @@ __EOT__
308368
fi
309369
done
310370

311-
if [ -n "${existing:-}" ]; then
312-
brew upgrade $existing
313-
fi
314-
if [ -n "${new:-}" ]; then
315-
brew install $new
316-
fi
371+
# Homebrew no longer builds Intel macOS bottles (tier 3); when a
372+
# bottle is missing, retry the formula as a source build.
373+
for dep in ${existing:-}; do
374+
brew upgrade "$dep" || brew upgrade --build-from-source "$dep"
375+
done
376+
for dep in ${new:-}; do
377+
brew install "$dep" || brew install --build-from-source "$dep"
378+
done
317379
else
318380
echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2
319381
exit 1
@@ -437,6 +499,7 @@ __EOT__
437499
install_toolchains() {
438500
# Get the latest toolchain release from https://github.com/qmk/qmk_toolchains
439501
local latest_toolchains_release=$(github_api_call repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
502+
check_release_tag qmk_toolchains "$latest_toolchains_release"
440503
# Download the specific release asset with a matching keyword
441504
local toolchain_url=$(github_api_call repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep -E "qmk_toolchains-.*$(fn_os)$(fn_arch)")
442505
if [ -z "$toolchain_url" ]; then
@@ -464,6 +527,7 @@ __EOT__
464527

465528
# Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils
466529
local latest_flashutils_release=$(github_api_call repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
530+
check_release_tag qmk_flashutils "$latest_flashutils_release"
467531
# Download the specific release asset with a matching keyword
468532
local flashutils_url=$(github_api_call repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep -E "qmk_flashutils-.*$osarchvariant")
469533
if [ -z "$flashutils_url" ]; then
@@ -486,10 +550,7 @@ __EOT__
486550
install_linux_udev_rules() {
487551
# Get the latest qmk_udev release
488552
local latest_udev_release=$(github_api_call repos/qmk/qmk_udev/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
489-
if [ -z "$latest_udev_release" ]; then
490-
echo "Could not determine latest qmk_udev release." >&2
491-
exit 1
492-
fi
553+
check_release_tag qmk_udev "$latest_udev_release"
493554
echo "Using qmk_udev release: $latest_udev_release" >&2
494555

495556
# Download the udev rules file
@@ -541,6 +602,7 @@ __EOT__
541602
install_windows_drivers() {
542603
# Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer
543604
local latest_driver_installer_release=$(github_api_call repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
605+
check_release_tag qmk_driver_installer "$latest_driver_installer_release"
544606
# Download the specific release asset
545607
local driver_installer_url=$(github_api_call repos/qmk/qmk_driver_installer/releases/tags/$latest_driver_installer_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep '\.exe')
546608
if [ -z "$driver_installer_url" ]; then
@@ -600,7 +662,14 @@ __EOT__
600662
setup_paths
601663

602664
# Work out where we want to install the distribution and tools now that `uv` is installed
603-
export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv_command run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)}")"
665+
export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv_command run --quiet --no-project --python $PYTHON_TARGET_VERSION --with platformdirs -)}")"
666+
667+
# `export` masks any failure of the `uv` invocation above, so bail out here
668+
# rather than continue with an empty directory.
669+
if [ -z "$QMK_DISTRIB_DIR" ]; then
670+
echo "Could not determine the QMK distribution directory." >&2
671+
exit 1
672+
fi
604673

605674
# Clear out the distrib directory if necessary
606675
if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then

0 commit comments

Comments
 (0)