From cb4043ec4cbed55c53a35d1a1d95f5f7cede2bd0 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 19 Mar 2025 12:05:06 +1100 Subject: [PATCH 01/40] Add environment bootstrap script. --- util/env-bootstrap.sh | 208 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 util/env-bootstrap.sh diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh new file mode 100755 index 00000000000..ee26c5a623e --- /dev/null +++ b/util/env-bootstrap.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash +# Copyright 2025 Nick Brassel (@tzarc) +# SPDX-License-Identifier: GPL-2.0-or-later +set -eu + +################################################################################ +# This script will install the QMK CLI, toolchains, and flashing utilities. +################################################################################ +# Configurables: +# QMK_DISTRIB_DIR: The directory to install the QMK distribution to. +# UV_INSTALL_DIR: The directory to install `uv` to. This will be ignored on QMK MSYS, installing into `/opt/uv` instead. +# SKIP_CLEAN: Skip cleaning the distribution directory. +# SKIP_UV: Skip installing `uv`. +# SKIP_QMK_CLI: Skip installing the QMK CLI. +# SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. +# SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. +################################################################################ +# Usage: +# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh +# +# An example which skips installing `uv`: +# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | SKIP_UV=1 sh +# +# Any other configurable items listed above may be specified in the same way. +################################################################################ + +# Work out where we want to install the distribution +QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/qmk/distrib} +# Clear out the target directory if necessary +if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then + if [ -d "$QMK_DISTRIB_DIR" ]; then + echo "Removing old QMK distribution..." >&2 + rm -rf "$QMK_DISTRIB_DIR" + fi +fi +mkdir -p "$QMK_DISTRIB_DIR" + +# Windows doesn't like `/tmp` so we need to set a different temporary directory +# and also set the `UV_INSTALL_DIR` to a location that doesn't pollute the user's +# home directory. +if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then + export TMPDIR="$(cygpath -w "$TMP")" + export UV_INSTALL_DIR=/opt/uv +fi + +download_url() { + local url=$1 + local filename=${2:-$(basename "$url")} + local quiet='' + if [ -n "$(command -v curl 2>/dev/null || true)" ]; then + [ $filename = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 + curl -LSf $quiet -o "$filename" "$url" + elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then + [ $filename = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 + wget $quiet "-O$filename" "$url" + else + echo "Please install 'curl' or 'wget' to continue." >&2 + exit 1 + fi +} + +fn_os() { + local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z') + if [ -z "$os_name" ]; then + os_name=$(uname -s | tr 'A-Z' 'a-z') + fi + case "$os_name" in + *darwin* | *macos* | *apple*) + echo macos + ;; + *windows* | *mingw* | *w64*) + echo windows + ;; + *linux*) + echo linux + ;; + *) + echo unknown + ;; + esac +} + +fn_arch() { + local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z') + if [ -z "$arch_name" ]; then + arch_name=$(uname -m | tr 'A-Z' 'a-z') + fi + case "$arch_name" in + *arm64* | *aarch64*) + echo ARM64 + ;; + *riscv64*) + echo RV64 + ;; + *x86_64* | *x64*) + echo X64 + ;; + *) + echo unknown + ;; + esac +} + +install_uv() { + # Install `uv` (or update as necessary) + download_url https://astral.sh/uv/install.sh - | TMPDIR=${TMPDIR:-} UV_INSTALL_DIR=${UV_INSTALL_DIR:-} sh + + # Set up the paths for any of the locations `uv` expects + if [ -n "${XDG_BIN_HOME:-}" ]; then + export PATH="$XDG_BIN_HOME:$PATH" + fi + if [ -n "${XDG_DATA_HOME:-}" ]; then + export PATH="$XDG_DATA_HOME/../bin:$PATH" + fi + [ ! -d "$HOME/.local/bin" ] || export PATH="$HOME/.local/bin:$PATH" + + if [ -n "${UV_INSTALL_DIR:-}" ]; then + export PATH="$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH" # cater for both "flat" and "hierarchical" installs of `uv` + fi +} + +install_qmk_cli() { + # Install the QMK CLI + uv tool install --force --with pip --upgrade --python 3.13 qmk + + # QMK is installed to... + local qmk_tooldir="$(uv tool dir)/qmk" + + # Convert it to a unix-style path if we're on Windows/Msys2 + if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then + qmk_tooldir="$(cygpath -u "$qmk_tooldir")" + fi + + # Activate the environment + if [ -e "$qmk_tooldir/bin" ]; then + . "$qmk_tooldir/bin/activate" + elif [ -e "$qmk_tooldir/Scripts" ]; then + . "$qmk_tooldir/Scripts/activate" + else + echo "Could not find the QMK environment to activate." >&2 + exit 1 + fi + + # Install the QMK dependencies + uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt + uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt + + # Deactivate the environment + deactivate +} + +install_toolchains() { + # Get the latest toolchain release from https://github.com/qmk/qmk_toolchains + local latest_toolchains_release=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') + # Download the specific release asset with a matching keyword + local toolchain_url=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) + if [ -z "$toolchain_url" ]; then + echo "No toolchain found for this OS/Arch combination." >&2 + exit 1 + fi + + # Download the toolchain release to the toolchains location + echo "Downloading compiler toolchain..." >&2 + local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")" + download_url "$toolchain_url" "$target_file" + + # Extract the toolchain + echo "Extracting compiler toolchain..." >&2 + tar xf "$target_file" -C "$QMK_DISTRIB_DIR" --strip-components=1 +} + +install_flashing_tools() { + # Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils + local latest_flashutils_release=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') + # Download the specific release asset with a matching keyword + local flashutils_url=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) + if [ -z "$flashutils_url" ]; then + echo "No flashing tools found for this OS/Arch combination." >&2 + exit 1 + fi + + # Download the flashing tools release to the toolchains location + echo "Downloading flashing tools..." >&2 + local target_file="$QMK_DISTRIB_DIR/$(basename "$flashutils_url")" + download_url "$flashutils_url" "$target_file" + + # Extract the flashing tools + echo "Extracting flashing tools..." >&2 + tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" +} + +clean_tarballs() { + # Clean up the tarballs + rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true +} + +[ -n "${SKIP_UV:-}" ] || install_uv +[ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli +[ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains +[ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools +clean_tarballs + +# Notify the user that they may need to restart their shell to get the `qmk` command +hash -r +echo +echo "You may need to restart your shell to gain access to the 'qmk' command." +echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" +echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" From ddd0f54c94e6b354dc2059bf37dffea53caf5573 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 19 Mar 2025 12:12:38 +1100 Subject: [PATCH 02/40] Hook up internals of the `qmk` CLI to use `$QMK_DISTRIB_DIR`. --- lib/python/qmk/cli/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 3f2ba9ce3cc..07959f0bd3e 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -12,6 +12,11 @@ from subprocess import run from milc import cli, __VERSION__ from milc.questions import yesno +# Ensure the QMK distribution is on the `$PATH` if present. +QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', os.path.expanduser('~/.local/qmk/distrib'))) # the expansion must be kept in sync with `util/env-bootstrap.sh`! +if QMK_DISTRIB_DIR.exists(): + os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH'] + import_names = { # A mapping of package name to importable name 'pep8-naming': 'pep8ext_naming', From 99f2b767be0fe706103562757caf5a6ea53a7da5 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 19 Mar 2025 14:01:02 +1100 Subject: [PATCH 03/40] More essential binaries. --- lib/python/qmk/cli/doctor/check.py | 53 +++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/python/qmk/cli/doctor/check.py b/lib/python/qmk/cli/doctor/check.py index 2804a1d7df1..2da9835efef 100644 --- a/lib/python/qmk/cli/doctor/check.py +++ b/lib/python/qmk/cli/doctor/check.py @@ -1,7 +1,6 @@ """Check for specific programs. """ from enum import Enum -import re import shutil from subprocess import DEVNULL, TimeoutExpired from tempfile import TemporaryDirectory @@ -18,6 +17,18 @@ class CheckStatus(Enum): ESSENTIAL_BINARIES = { + 'make': { + 'version_arg': '--version' + }, + 'git': { + 'version_arg': '--version' + }, + 'dos2unix': { + 'version_arg': '--version' + }, + 'diff': { + 'version_arg': '--version' + }, 'dfu-programmer': {}, 'avrdude': {}, 'dfu-util': {}, @@ -30,14 +41,36 @@ ESSENTIAL_BINARIES = { } -def _parse_gcc_version(version): - m = re.match(r"(\d+)(?:\.(\d+))?(?:\.(\d+))?", version) +def _check_make_version(): + last_line = ESSENTIAL_BINARIES['make']['output'].split('\n')[0] + version_number = last_line.split()[2] + cli.log.info('Found make version %s', version_number) - return { - 'major': int(m.group(1)), - 'minor': int(m.group(2)) if m.group(2) else 0, - 'patch': int(m.group(3)) if m.group(3) else 0, - } + return CheckStatus.OK + + +def _check_git_version(): + last_line = ESSENTIAL_BINARIES['git']['output'].split('\n')[0] + version_number = last_line.split()[2] + cli.log.info('Found git version %s', version_number) + + return CheckStatus.OK + + +def _check_dos2unix_version(): + last_line = ESSENTIAL_BINARIES['dos2unix']['output'].split('\n')[0] + version_number = last_line.split()[1] + cli.log.info('Found dos2unix version %s', version_number) + + return CheckStatus.OK + + +def _check_diff_version(): + last_line = ESSENTIAL_BINARIES['diff']['output'].split('\n')[0] + version_number = last_line.split()[3] + cli.log.info('Found diff version %s', version_number) + + return CheckStatus.OK def _check_arm_gcc_version(): @@ -159,6 +192,10 @@ def check_binary_versions(): """Check the versions of ESSENTIAL_BINARIES """ checks = { + 'make': _check_make_version, + 'git': _check_git_version, + 'dos2unix': _check_dos2unix_version, + 'diff': _check_diff_version, 'arm-none-eabi-gcc': _check_arm_gcc_version, 'avr-gcc': _check_avr_gcc_version, 'avrdude': _check_avrdude_version, From ad8c332b593ac2e275cdd918672ee3d3336b877a Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 20 Mar 2025 23:53:19 +1100 Subject: [PATCH 04/40] Distribution path. --- lib/python/qmk/cli/__init__.py | 5 ++++- util/env-bootstrap.sh | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 07959f0bd3e..314b0489e93 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -3,6 +3,8 @@ We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup. """ import os +import platform +import platformdirs import shlex import sys from importlib.util import find_spec @@ -13,7 +15,8 @@ from milc import cli, __VERSION__ from milc.questions import yesno # Ensure the QMK distribution is on the `$PATH` if present. -QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', os.path.expanduser('~/.local/qmk/distrib'))) # the expansion must be kept in sync with `util/env-bootstrap.sh`! +_default_distrib_path = '/opt/qmk' if 'windows' in platform.platform().lower() else platformdirs.user_data_path('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`! +QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _default_distrib_path)) if QMK_DISTRIB_DIR.exists(): os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH'] diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index ee26c5a623e..85fb0f8db71 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -8,7 +8,7 @@ set -eu ################################################################################ # Configurables: # QMK_DISTRIB_DIR: The directory to install the QMK distribution to. -# UV_INSTALL_DIR: The directory to install `uv` to. This will be ignored on QMK MSYS, installing into `/opt/uv` instead. +# UV_INSTALL_DIR: The directory to install `uv` to. # SKIP_CLEAN: Skip cleaning the distribution directory. # SKIP_UV: Skip installing `uv`. # SKIP_QMK_CLI: Skip installing the QMK CLI. @@ -24,8 +24,16 @@ set -eu # Any other configurable items listed above may be specified in the same way. ################################################################################ +# Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. +# Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. +if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then + export TMPDIR="$(cygpath -w "$TMP")" + export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} +fi + # Work out where we want to install the distribution -QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/qmk/distrib} +export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} # Clear out the target directory if necessary if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then if [ -d "$QMK_DISTRIB_DIR" ]; then @@ -35,14 +43,6 @@ if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK fi mkdir -p "$QMK_DISTRIB_DIR" -# Windows doesn't like `/tmp` so we need to set a different temporary directory -# and also set the `UV_INSTALL_DIR` to a location that doesn't pollute the user's -# home directory. -if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - export TMPDIR="$(cygpath -w "$TMP")" - export UV_INSTALL_DIR=/opt/uv -fi - download_url() { local url=$1 local filename=${2:-$(basename "$url")} From 6c92b08d8f619f7db086bf7787792da9129198f9 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 21 Mar 2025 00:07:35 +1100 Subject: [PATCH 05/40] Update __init__.py --- lib/python/qmk/cli/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 314b0489e93..8ce14b4ee7b 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -15,7 +15,7 @@ from milc import cli, __VERSION__ from milc.questions import yesno # Ensure the QMK distribution is on the `$PATH` if present. -_default_distrib_path = '/opt/qmk' if 'windows' in platform.platform().lower() else platformdirs.user_data_path('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`! +_default_distrib_path = '/opt/qmk' if 'windows' in platform.platform().lower() else platformdirs.user_data_dir('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`! QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _default_distrib_path)) if QMK_DISTRIB_DIR.exists(): os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH'] From fa170a7c153f739d22be8780f7345aa6e15c7917 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 27 Mar 2025 21:41:02 +1100 Subject: [PATCH 06/40] Basic package manager stuff on macOS/Linux. --- util/env-bootstrap.sh | 97 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 85fb0f8db71..902e10d9a7d 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash # Copyright 2025 Nick Brassel (@tzarc) # SPDX-License-Identifier: GPL-2.0-or-later + +{ # this ensures the entire script is downloaded # + set -eu ################################################################################ @@ -101,6 +104,96 @@ fn_arch() { esac } +check_yesno() { + read -p "Proceed? [y/N] " res /dev/null || true)" ]; then + echo "It will also install the following system packages using 'brew':" >&2 + echo " zstd clang-format make hidapi libusb" >&2 + check_yesno || exit 1 + brew update && brew upgrade --formulae + brew install zstd clang-format make hidapi libusb + else + echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 + exit 1 + fi + ;; + linux) + case $(grep ID /etc/os-release) in + *arch* | *manjaro*) + echo "It will also install the following system packages using 'pacman':" >&2 + echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 + check_yesno || exit 1 + sudo pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip + sudo pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled + ;; + *debian* | *ubuntu*) + echo "It will also install the following system packages using 'apt':" >&2 + echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 + check_yesno || exit 1 + sudo apt-get update + DEBIAN_FRONTEND=noninteractive \ + sudo apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 + ;; + *fedora*) + echo "It will also install the following system packages using 'dnf':" >&2 + echo " zstd clang diffutils gcc git unzip wget zip hidapi" >&2 + echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 + echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 + check_yesno || exit 1 + sudo dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable + ;; + *gentoo*) + echo "It will also the following packages using 'emerge':" >&2 + echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 + check_yesno || exit 1 + sudo emerge -au --noreplace \ + app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi + ;; + *slackware*) + echo "It will also the following packages using 'sboinstall':" >&2 + echo " python3" >&2 + check_yesno || exit 1 + sudo sboinstall python3 # Rest tbd? + ;; + *solus*) + echo "It will also install the following system packages using 'eopkg':" >&2 + echo " system.devel zstd git wget zip unzip python3" >&2 + check_yesno || exit 1 + sudo eopkg -y update-repo + sudo eopkg -y upgrade + sudo eopkg -y install \ + -c system.devel zstd git wget zip unzip python3 + ;; + *void*) + echo "It will also the following packages using 'xbps-install':" >&2 + echo " zstd git make wget unzip zip python3" >&2 + check_yesno || exit 1 + sudo xbps-install -y \ + zstd git make wget unzip zip python3 + ;; + *) + echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" + echo + echo "https://docs.qmk.fm/#/getting_started_docker" + exit 1 + ;; + esac + ;; + esac +} + install_uv() { # Install `uv` (or update as necessary) download_url https://astral.sh/uv/install.sh - | TMPDIR=${TMPDIR:-} UV_INSTALL_DIR=${UV_INSTALL_DIR:-} sh @@ -194,6 +287,8 @@ clean_tarballs() { rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true } +echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." +[ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains @@ -206,3 +301,5 @@ echo echo "You may need to restart your shell to gain access to the 'qmk' command." echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" + +} # this ensures the entire script is downloaded # From 4ef00cd6e89656d13eb0936b81d889fd76330876 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 27 Mar 2025 22:36:37 +1100 Subject: [PATCH 07/40] Argument parsing. --- util/env-bootstrap.sh | 137 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 902e10d9a7d..286da4f415c 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -9,42 +9,99 @@ set -eu ################################################################################ # This script will install the QMK CLI, toolchains, and flashing utilities. ################################################################################ -# Configurables: -# QMK_DISTRIB_DIR: The directory to install the QMK distribution to. -# UV_INSTALL_DIR: The directory to install `uv` to. -# SKIP_CLEAN: Skip cleaning the distribution directory. -# SKIP_UV: Skip installing `uv`. -# SKIP_QMK_CLI: Skip installing the QMK CLI. -# SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. -# SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. +# Environment variables: +# YES: Assume "yes" for all prompts. (or: --yes) +# QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...) +# UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...) +# SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean) +# SKIP_PACKAGE_MANAGER: Skip installing the necessary packages for the package manager. (or: --skip-package-manager) +# SKIP_UV: Skip installing `uv`. (or: --skip-uv) +# SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli) +# SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains) +# SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils) +# +# Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`). ################################################################################ # Usage: # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh # -# An example which skips installing `uv`: +# Help: +# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --help +# +# An example which skips installing `uv` using environment variables: # curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | SKIP_UV=1 sh # +# ...or by using command line arguments: +# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --skip-uv +# # Any other configurable items listed above may be specified in the same way. ################################################################################ -# Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. -# Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. -if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - export TMPDIR="$(cygpath -w "$TMP")" - export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} -fi +# Work out which `sed` to use +command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed -# Work out where we want to install the distribution -export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} -# Clear out the target directory if necessary -if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then - if [ -d "$QMK_DISTRIB_DIR" ]; then - echo "Removing old QMK distribution..." >&2 - rm -rf "$QMK_DISTRIB_DIR" - fi -fi -mkdir -p "$QMK_DISTRIB_DIR" +script_args() { + cat <<__EOT__ + --help -- Shows this help text + --yes -- Assumes "yes" for all prompts + --uv-install-dir={path} -- The directory to install \`uv\` into + --qmk-distrib-dir={path} -- The directory to install the QMK distribution into + --skip-clean -- Skip cleaning the QMK distribution directory + --skip-package-manager -- Skip installing the necessary packages for the package manager + --skip-uv -- Skip installing \`uv\` + --skip-qmk-cli -- Skip installing the QMK CLI + --skip-qmk-toolchains -- Skip installing the QMK toolchains + --skip-qmk-flashutils -- Skip installing the QMK flashing utilities +__EOT__ +} + +script_help() { + echo "$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\s*@@g' -e 's@\s\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\n' ' ')" + echo + echo "Arguments:" + script_args + echo + echo "Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean')." +} + +script_parse_args() { + local N + local V + while [[ ! -z "${1:-}" ]]; do + case "$1" in + --help) + script_help + exit 0 + ;; + --*=*) + N=${1%%=*} + N=${N##--} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + V=${1##*=} + export $N="$V" + ;; + --no-*) + N=${1##--no-} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + unset $N + ;; + --*) + N=${1##--} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + export $N=true + ;; + *) + echo "Unknown argument: '$1'" >&2 + echo + script_help >&2 + exit 1 + ;; + esac + shift + unset N + unset V + done +} download_url() { local url=$1 @@ -105,6 +162,7 @@ fn_arch() { } check_yesno() { + [ -z "${YES:-}" ] || return 0 read -p "Proceed? [y/N] " res /dev/null || true)" = "Msys" ]; then + export TMPDIR="$(cygpath -w "$TMP")" + export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} +fi + +# Work out where we want to install the distribution +export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} + +script_parse_args "$@" + echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." +[ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { check_yesno || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv +setup_paths [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli + +# Clear out the distrib directory if necessary +if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then + if [ -d "$QMK_DISTRIB_DIR" ]; then + echo "Removing old QMK distribution..." >&2 + rm -rf "$QMK_DISTRIB_DIR" + fi +fi +mkdir -p "$QMK_DISTRIB_DIR" + [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools clean_tarballs From 1921a4808ffd8cfa1ea5b52e5aa4fbcee7280518 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 27 Mar 2025 22:38:39 +1100 Subject: [PATCH 08/40] Formatting. --- util/env-bootstrap.sh | 634 +++++++++++++++++++++--------------------- 1 file changed, 317 insertions(+), 317 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 286da4f415c..43c82a8fd2e 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -2,10 +2,6 @@ # Copyright 2025 Nick Brassel (@tzarc) # SPDX-License-Identifier: GPL-2.0-or-later -{ # this ensures the entire script is downloaded # - -set -eu - ################################################################################ # This script will install the QMK CLI, toolchains, and flashing utilities. ################################################################################ @@ -37,11 +33,15 @@ set -eu # Any other configurable items listed above may be specified in the same way. ################################################################################ -# Work out which `sed` to use -command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed +{ # this ensures the entire script is downloaded # -script_args() { - cat <<__EOT__ + set -eu + + # Work out which `sed` to use + command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed + + script_args() { + cat <<__EOT__ --help -- Shows this help text --yes -- Assumes "yes" for all prompts --uv-install-dir={path} -- The directory to install \`uv\` into @@ -53,338 +53,338 @@ script_args() { --skip-qmk-toolchains -- Skip installing the QMK toolchains --skip-qmk-flashutils -- Skip installing the QMK flashing utilities __EOT__ -} + } -script_help() { - echo "$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\s*@@g' -e 's@\s\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\n' ' ')" - echo - echo "Arguments:" - script_args - echo - echo "Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean')." -} + script_help() { + echo "$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\s*@@g' -e 's@\s\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\n' ' ')" + echo + echo "Arguments:" + script_args + echo + echo "Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean')." + } -script_parse_args() { - local N - local V - while [[ ! -z "${1:-}" ]]; do - case "$1" in - --help) - script_help - exit 0 - ;; - --*=*) - N=${1%%=*} - N=${N##--} - N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') - V=${1##*=} - export $N="$V" - ;; - --no-*) - N=${1##--no-} - N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') - unset $N - ;; - --*) - N=${1##--} - N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') - export $N=true - ;; - *) - echo "Unknown argument: '$1'" >&2 - echo - script_help >&2 - exit 1 - ;; - esac - shift - unset N - unset V - done -} + script_parse_args() { + local N + local V + while [[ ! -z "${1:-}" ]]; do + case "$1" in + --help) + script_help + exit 0 + ;; + --*=*) + N=${1%%=*} + N=${N##--} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + V=${1##*=} + export $N="$V" + ;; + --no-*) + N=${1##--no-} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + unset $N + ;; + --*) + N=${1##--} + N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z') + export $N=true + ;; + *) + echo "Unknown argument: '$1'" >&2 + echo + script_help >&2 + exit 1 + ;; + esac + shift + unset N + unset V + done + } -download_url() { - local url=$1 - local filename=${2:-$(basename "$url")} - local quiet='' - if [ -n "$(command -v curl 2>/dev/null || true)" ]; then - [ $filename = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 - curl -LSf $quiet -o "$filename" "$url" - elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then - [ $filename = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 - wget $quiet "-O$filename" "$url" - else - echo "Please install 'curl' or 'wget' to continue." >&2 - exit 1 - fi -} - -fn_os() { - local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z') - if [ -z "$os_name" ]; then - os_name=$(uname -s | tr 'A-Z' 'a-z') - fi - case "$os_name" in - *darwin* | *macos* | *apple*) - echo macos - ;; - *windows* | *mingw* | *w64*) - echo windows - ;; - *linux*) - echo linux - ;; - *) - echo unknown - ;; - esac -} - -fn_arch() { - local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z') - if [ -z "$arch_name" ]; then - arch_name=$(uname -m | tr 'A-Z' 'a-z') - fi - case "$arch_name" in - *arm64* | *aarch64*) - echo ARM64 - ;; - *riscv64*) - echo RV64 - ;; - *x86_64* | *x64*) - echo X64 - ;; - *) - echo unknown - ;; - esac -} - -check_yesno() { - [ -z "${YES:-}" ] || return 0 - read -p "Proceed? [y/N] " res /dev/null || true)" ]; then - echo "It will also install the following system packages using 'brew':" >&2 - echo " zstd clang-format make hidapi libusb" >&2 - check_yesno || exit 1 - brew update && brew upgrade --formulae - brew install zstd clang-format make hidapi libusb + download_url() { + local url=$1 + local filename=${2:-$(basename "$url")} + local quiet='' + if [ -n "$(command -v curl 2>/dev/null || true)" ]; then + [ $filename = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 + curl -LSf $quiet -o "$filename" "$url" + elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then + [ $filename = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 + wget $quiet "-O$filename" "$url" else - echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 + echo "Please install 'curl' or 'wget' to continue." >&2 exit 1 fi - ;; - linux) - case $(grep ID /etc/os-release) in - *arch* | *manjaro*) - echo "It will also install the following system packages using 'pacman':" >&2 - echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 - check_yesno || exit 1 - sudo pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip - sudo pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled + } + + fn_os() { + local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z') + if [ -z "$os_name" ]; then + os_name=$(uname -s | tr 'A-Z' 'a-z') + fi + case "$os_name" in + *darwin* | *macos* | *apple*) + echo macos ;; - *debian* | *ubuntu*) - echo "It will also install the following system packages using 'apt':" >&2 - echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 - check_yesno || exit 1 - sudo apt-get update - DEBIAN_FRONTEND=noninteractive \ - sudo apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 + *windows* | *mingw* | *w64*) + echo windows ;; - *fedora*) - echo "It will also install the following system packages using 'dnf':" >&2 - echo " zstd clang diffutils gcc git unzip wget zip hidapi" >&2 - echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 - echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 - check_yesno || exit 1 - sudo dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable - ;; - *gentoo*) - echo "It will also the following packages using 'emerge':" >&2 - echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 - check_yesno || exit 1 - sudo emerge -au --noreplace \ - app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi - ;; - *slackware*) - echo "It will also the following packages using 'sboinstall':" >&2 - echo " python3" >&2 - check_yesno || exit 1 - sudo sboinstall python3 # Rest tbd? - ;; - *solus*) - echo "It will also install the following system packages using 'eopkg':" >&2 - echo " system.devel zstd git wget zip unzip python3" >&2 - check_yesno || exit 1 - sudo eopkg -y update-repo - sudo eopkg -y upgrade - sudo eopkg -y install \ - -c system.devel zstd git wget zip unzip python3 - ;; - *void*) - echo "It will also the following packages using 'xbps-install':" >&2 - echo " zstd git make wget unzip zip python3" >&2 - check_yesno || exit 1 - sudo xbps-install -y \ - zstd git make wget unzip zip python3 + *linux*) + echo linux ;; *) - echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" - echo - echo "https://docs.qmk.fm/#/getting_started_docker" - exit 1 + echo unknown ;; esac - ;; - esac -} + } -install_uv() { - # Install `uv` (or update as necessary) - download_url https://astral.sh/uv/install.sh - | TMPDIR=${TMPDIR:-} UV_INSTALL_DIR=${UV_INSTALL_DIR:-} sh -} + fn_arch() { + local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z') + if [ -z "$arch_name" ]; then + arch_name=$(uname -m | tr 'A-Z' 'a-z') + fi + case "$arch_name" in + *arm64* | *aarch64*) + echo ARM64 + ;; + *riscv64*) + echo RV64 + ;; + *x86_64* | *x64*) + echo X64 + ;; + *) + echo unknown + ;; + esac + } -setup_paths() { - # Set up the paths for any of the locations `uv` expects - if [ -n "${XDG_BIN_HOME:-}" ]; then - export PATH="$XDG_BIN_HOME:$PATH" - fi - if [ -n "${XDG_DATA_HOME:-}" ]; then - export PATH="$XDG_DATA_HOME/../bin:$PATH" - fi - [ ! -d "$HOME/.local/bin" ] || export PATH="$HOME/.local/bin:$PATH" + check_yesno() { + [ -z "${YES:-}" ] || return 0 + read -p "Proceed? [y/N] " res /dev/null || true)" ]; then + echo "It will also install the following system packages using 'brew':" >&2 + echo " zstd clang-format make hidapi libusb" >&2 + check_yesno || exit 1 + brew update && brew upgrade --formulae + brew install zstd clang-format make hidapi libusb + else + echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 + exit 1 + fi + ;; + linux) + case $(grep ID /etc/os-release) in + *arch* | *manjaro*) + echo "It will also install the following system packages using 'pacman':" >&2 + echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 + check_yesno || exit 1 + sudo pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip + sudo pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled + ;; + *debian* | *ubuntu*) + echo "It will also install the following system packages using 'apt':" >&2 + echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 + check_yesno || exit 1 + sudo apt-get update + DEBIAN_FRONTEND=noninteractive \ + sudo apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 + ;; + *fedora*) + echo "It will also install the following system packages using 'dnf':" >&2 + echo " zstd clang diffutils gcc git unzip wget zip hidapi" >&2 + echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 + echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 + check_yesno || exit 1 + sudo dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable + ;; + *gentoo*) + echo "It will also the following packages using 'emerge':" >&2 + echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 + check_yesno || exit 1 + sudo emerge -au --noreplace \ + app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi + ;; + *slackware*) + echo "It will also the following packages using 'sboinstall':" >&2 + echo " python3" >&2 + check_yesno || exit 1 + sudo sboinstall python3 # Rest tbd? + ;; + *solus*) + echo "It will also install the following system packages using 'eopkg':" >&2 + echo " system.devel zstd git wget zip unzip python3" >&2 + check_yesno || exit 1 + sudo eopkg -y update-repo + sudo eopkg -y upgrade + sudo eopkg -y install \ + -c system.devel zstd git wget zip unzip python3 + ;; + *void*) + echo "It will also the following packages using 'xbps-install':" >&2 + echo " zstd git make wget unzip zip python3" >&2 + check_yesno || exit 1 + sudo xbps-install -y \ + zstd git make wget unzip zip python3 + ;; + *) + echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" + echo + echo "https://docs.qmk.fm/#/getting_started_docker" + exit 1 + ;; + esac + ;; + esac + } -install_qmk_cli() { - # Install the QMK CLI - uv tool install --force --with pip --upgrade --python 3.13 qmk + install_uv() { + # Install `uv` (or update as necessary) + download_url https://astral.sh/uv/install.sh - | TMPDIR=${TMPDIR:-} UV_INSTALL_DIR=${UV_INSTALL_DIR:-} sh + } - # QMK is installed to... - local qmk_tooldir="$(uv tool dir)/qmk" + setup_paths() { + # Set up the paths for any of the locations `uv` expects + if [ -n "${XDG_BIN_HOME:-}" ]; then + export PATH="$XDG_BIN_HOME:$PATH" + fi + if [ -n "${XDG_DATA_HOME:-}" ]; then + export PATH="$XDG_DATA_HOME/../bin:$PATH" + fi + [ ! -d "$HOME/.local/bin" ] || export PATH="$HOME/.local/bin:$PATH" - # Convert it to a unix-style path if we're on Windows/Msys2 + if [ -n "${UV_INSTALL_DIR:-}" ]; then + export PATH="$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH" # cater for both "flat" and "hierarchical" installs of `uv` + fi + } + + install_qmk_cli() { + # Install the QMK CLI + uv tool install --force --with pip --upgrade --python 3.13 qmk + + # QMK is installed to... + local qmk_tooldir="$(uv tool dir)/qmk" + + # Convert it to a unix-style path if we're on Windows/Msys2 + if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then + qmk_tooldir="$(cygpath -u "$qmk_tooldir")" + fi + + # Activate the environment + if [ -e "$qmk_tooldir/bin" ]; then + . "$qmk_tooldir/bin/activate" + elif [ -e "$qmk_tooldir/Scripts" ]; then + . "$qmk_tooldir/Scripts/activate" + else + echo "Could not find the QMK environment to activate." >&2 + exit 1 + fi + + # Install the QMK dependencies + uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt + uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt + + # Deactivate the environment + deactivate + } + + install_toolchains() { + # Get the latest toolchain release from https://github.com/qmk/qmk_toolchains + local latest_toolchains_release=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') + # Download the specific release asset with a matching keyword + local toolchain_url=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) + if [ -z "$toolchain_url" ]; then + echo "No toolchain found for this OS/Arch combination." >&2 + exit 1 + fi + + # Download the toolchain release to the toolchains location + echo "Downloading compiler toolchain..." >&2 + local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")" + download_url "$toolchain_url" "$target_file" + + # Extract the toolchain + echo "Extracting compiler toolchain..." >&2 + tar xf "$target_file" -C "$QMK_DISTRIB_DIR" --strip-components=1 + } + + install_flashing_tools() { + # Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils + local latest_flashutils_release=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') + # Download the specific release asset with a matching keyword + local flashutils_url=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) + if [ -z "$flashutils_url" ]; then + echo "No flashing tools found for this OS/Arch combination." >&2 + exit 1 + fi + + # Download the flashing tools release to the toolchains location + echo "Downloading flashing tools..." >&2 + local target_file="$QMK_DISTRIB_DIR/$(basename "$flashutils_url")" + download_url "$flashutils_url" "$target_file" + + # Extract the flashing tools + echo "Extracting flashing tools..." >&2 + tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" + } + + clean_tarballs() { + # Clean up the tarballs + rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true + } + + # Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. + # Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - qmk_tooldir="$(cygpath -u "$qmk_tooldir")" + export TMPDIR="$(cygpath -w "$TMP")" + export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} fi - # Activate the environment - if [ -e "$qmk_tooldir/bin" ]; then - . "$qmk_tooldir/bin/activate" - elif [ -e "$qmk_tooldir/Scripts" ]; then - . "$qmk_tooldir/Scripts/activate" - else - echo "Could not find the QMK environment to activate." >&2 - exit 1 + # Work out where we want to install the distribution + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} + + script_parse_args "$@" + + echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." + [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { check_yesno || exit 1; } + [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps + [ -n "${SKIP_UV:-}" ] || install_uv + setup_paths + [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli + + # Clear out the distrib directory if necessary + if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then + if [ -d "$QMK_DISTRIB_DIR" ]; then + echo "Removing old QMK distribution..." >&2 + rm -rf "$QMK_DISTRIB_DIR" + fi fi + mkdir -p "$QMK_DISTRIB_DIR" - # Install the QMK dependencies - uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt - uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt + [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains + [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools + clean_tarballs - # Deactivate the environment - deactivate -} - -install_toolchains() { - # Get the latest toolchain release from https://github.com/qmk/qmk_toolchains - local latest_toolchains_release=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') - # Download the specific release asset with a matching keyword - local toolchain_url=$(download_url https://api.github.com/repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) - if [ -z "$toolchain_url" ]; then - echo "No toolchain found for this OS/Arch combination." >&2 - exit 1 - fi - - # Download the toolchain release to the toolchains location - echo "Downloading compiler toolchain..." >&2 - local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")" - download_url "$toolchain_url" "$target_file" - - # Extract the toolchain - echo "Extracting compiler toolchain..." >&2 - tar xf "$target_file" -C "$QMK_DISTRIB_DIR" --strip-components=1 -} - -install_flashing_tools() { - # Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils - local latest_flashutils_release=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') - # Download the specific release asset with a matching keyword - local flashutils_url=$(download_url https://api.github.com/repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch)) - if [ -z "$flashutils_url" ]; then - echo "No flashing tools found for this OS/Arch combination." >&2 - exit 1 - fi - - # Download the flashing tools release to the toolchains location - echo "Downloading flashing tools..." >&2 - local target_file="$QMK_DISTRIB_DIR/$(basename "$flashutils_url")" - download_url "$flashutils_url" "$target_file" - - # Extract the flashing tools - echo "Extracting flashing tools..." >&2 - tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" -} - -clean_tarballs() { - # Clean up the tarballs - rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true -} - -# Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. -# Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. -if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - export TMPDIR="$(cygpath -w "$TMP")" - export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} -fi - -# Work out where we want to install the distribution -export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} - -script_parse_args "$@" - -echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." -[ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { check_yesno || exit 1; } -[ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps -[ -n "${SKIP_UV:-}" ] || install_uv -setup_paths -[ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli - -# Clear out the distrib directory if necessary -if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then - if [ -d "$QMK_DISTRIB_DIR" ]; then - echo "Removing old QMK distribution..." >&2 - rm -rf "$QMK_DISTRIB_DIR" - fi -fi -mkdir -p "$QMK_DISTRIB_DIR" - -[ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains -[ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools -clean_tarballs - -# Notify the user that they may need to restart their shell to get the `qmk` command -hash -r -echo -echo "You may need to restart your shell to gain access to the 'qmk' command." -echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" -echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" + # Notify the user that they may need to restart their shell to get the `qmk` command + hash -r + echo + echo "You may need to restart your shell to gain access to the 'qmk' command." + echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" + echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" } # this ensures the entire script is downloaded # From 99bf22a50e0d99f08cac2e1a8ca95a1dcfbc3e63 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 28 Mar 2025 22:52:52 +1100 Subject: [PATCH 09/40] Windows doesn't like `/dev/tty`, pivot to delay with Ctrl-C prompt. --- util/env-bootstrap.sh | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 43c82a8fd2e..574a5e20966 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -6,7 +6,7 @@ # This script will install the QMK CLI, toolchains, and flashing utilities. ################################################################################ # Environment variables: -# YES: Assume "yes" for all prompts. (or: --yes) +# QUICK: Skip the pre-install delay. (or: --quick) # QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...) # UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...) # SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean) @@ -43,7 +43,7 @@ script_args() { cat <<__EOT__ --help -- Shows this help text - --yes -- Assumes "yes" for all prompts + --quick -- Skips the delay before installation --uv-install-dir={path} -- The directory to install \`uv\` into --qmk-distrib-dir={path} -- The directory to install the QMK distribution into --skip-clean -- Skip cleaning the QMK distribution directory @@ -161,15 +161,11 @@ __EOT__ esac } - check_yesno() { - [ -z "${YES:-}" ] || return 0 - read -p "Proceed? [y/N] " res &2 + echo "Waiting 10 seconds before proceeding. Press Ctrl+C to cancel installation." >&2 + sleep 10 } install_package_manager_deps() { @@ -179,7 +175,7 @@ __EOT__ if [ -n "$(command -v brew 2>/dev/null || true)" ]; then echo "It will also install the following system packages using 'brew':" >&2 echo " zstd clang-format make hidapi libusb" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 brew update && brew upgrade --formulae brew install zstd clang-format make hidapi libusb else @@ -192,14 +188,14 @@ __EOT__ *arch* | *manjaro*) echo "It will also install the following system packages using 'pacman':" >&2 echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip sudo pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled ;; *debian* | *ubuntu*) echo "It will also install the following system packages using 'apt':" >&2 echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo apt-get update DEBIAN_FRONTEND=noninteractive \ sudo apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 @@ -209,26 +205,26 @@ __EOT__ echo " zstd clang diffutils gcc git unzip wget zip hidapi" >&2 echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable ;; *gentoo*) echo "It will also the following packages using 'emerge':" >&2 echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo emerge -au --noreplace \ app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi ;; *slackware*) echo "It will also the following packages using 'sboinstall':" >&2 echo " python3" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo sboinstall python3 # Rest tbd? ;; *solus*) echo "It will also install the following system packages using 'eopkg':" >&2 echo " system.devel zstd git wget zip unzip python3" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo eopkg -y update-repo sudo eopkg -y upgrade sudo eopkg -y install \ @@ -237,7 +233,7 @@ __EOT__ *void*) echo "It will also the following packages using 'xbps-install':" >&2 echo " zstd git make wget unzip zip python3" >&2 - check_yesno || exit 1 + preinstall_delay || exit 1 sudo xbps-install -y \ zstd git make wget unzip zip python3 ;; @@ -318,7 +314,7 @@ __EOT__ download_url "$toolchain_url" "$target_file" # Extract the toolchain - echo "Extracting compiler toolchain..." >&2 + echo "Extracting compiler toolchain to '$QMK_DISTRIB_DIR'..." >&2 tar xf "$target_file" -C "$QMK_DISTRIB_DIR" --strip-components=1 } @@ -338,7 +334,7 @@ __EOT__ download_url "$flashutils_url" "$target_file" # Extract the flashing tools - echo "Extracting flashing tools..." >&2 + echo "Extracting flashing tools to '$QMK_DISTRIB_DIR'..." >&2 tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" } @@ -361,7 +357,7 @@ __EOT__ script_parse_args "$@" echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." - [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { check_yesno || exit 1; } + [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv setup_paths From 73b0a6d37c845e23e07d31231ee362a2479a0488 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 30 Mar 2025 00:24:12 +1100 Subject: [PATCH 10/40] GHA. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 574a5e20966..a2182938f9d 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -67,7 +67,7 @@ __EOT__ script_parse_args() { local N local V - while [[ ! -z "${1:-}" ]]; do + while [ ! -z "${1:-}" ]; do case "$1" in --help) script_help From 0d18136fdb7549cc7550c4a13c3fa6e5991e1f78 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 30 Mar 2025 00:28:06 +1100 Subject: [PATCH 11/40] Use `--confirm` or `CONFIRM=1` to skip delay, fixup use of `sudo` when unavailable as root. --- util/env-bootstrap.sh | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index a2182938f9d..813352ab9da 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -6,7 +6,7 @@ # This script will install the QMK CLI, toolchains, and flashing utilities. ################################################################################ # Environment variables: -# QUICK: Skip the pre-install delay. (or: --quick) +# CONFIRM: Skip the pre-install delay. (or: --confirm) # QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...) # UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...) # SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean) @@ -43,7 +43,7 @@ script_args() { cat <<__EOT__ --help -- Shows this help text - --quick -- Skips the delay before installation + --confirm -- Skips the delay before installation --uv-install-dir={path} -- The directory to install \`uv\` into --qmk-distrib-dir={path} -- The directory to install the QMK distribution into --skip-clean -- Skip cleaning the QMK distribution directory @@ -103,6 +103,12 @@ __EOT__ done } + nsudo() { + [[ ${EUID} -ne 0 ]] && echo "sudo" + true + } + + download_url() { local url=$1 local filename=${2:-$(basename "$url")} @@ -162,7 +168,7 @@ __EOT__ } preinstall_delay() { - [ -z "${QUICK:-}" ] || return 0 + [ -z "${CONFIRM:-}" ] || return 0 echo >&2 echo "Waiting 10 seconds before proceeding. Press Ctrl+C to cancel installation." >&2 sleep 10 @@ -189,16 +195,16 @@ __EOT__ echo "It will also install the following system packages using 'pacman':" >&2 echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 preinstall_delay || exit 1 - sudo pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip - sudo pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled + $(nsudo) pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip + $(nsudo) pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled ;; *debian* | *ubuntu*) echo "It will also install the following system packages using 'apt':" >&2 echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 preinstall_delay || exit 1 - sudo apt-get update + $(nsudo) apt-get update DEBIAN_FRONTEND=noninteractive \ - sudo apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 + $(nsudo) apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 ;; *fedora*) echo "It will also install the following system packages using 'dnf':" >&2 @@ -206,35 +212,35 @@ __EOT__ echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 preinstall_delay || exit 1 - sudo dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable + $(nsudo) dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable ;; *gentoo*) echo "It will also the following packages using 'emerge':" >&2 echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 preinstall_delay || exit 1 - sudo emerge -au --noreplace \ + $(nsudo) emerge -au --noreplace \ app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi ;; *slackware*) echo "It will also the following packages using 'sboinstall':" >&2 echo " python3" >&2 preinstall_delay || exit 1 - sudo sboinstall python3 # Rest tbd? + $(nsudo) sboinstall python3 # Rest tbd? ;; *solus*) echo "It will also install the following system packages using 'eopkg':" >&2 echo " system.devel zstd git wget zip unzip python3" >&2 preinstall_delay || exit 1 - sudo eopkg -y update-repo - sudo eopkg -y upgrade - sudo eopkg -y install \ + $(nsudo) eopkg -y update-repo + $(nsudo) eopkg -y upgrade + $(nsudo) eopkg -y install \ -c system.devel zstd git wget zip unzip python3 ;; *void*) echo "It will also the following packages using 'xbps-install':" >&2 echo " zstd git make wget unzip zip python3" >&2 preinstall_delay || exit 1 - sudo xbps-install -y \ + $(nsudo) xbps-install -y \ zstd git make wget unzip zip python3 ;; *) From 9b201d362c45cb4979fc6764a4732802a68d8a20 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 2 Apr 2025 16:41:15 +1100 Subject: [PATCH 12/40] Spaaaaaaaaace --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 813352ab9da..309606886f2 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -43,7 +43,7 @@ script_args() { cat <<__EOT__ --help -- Shows this help text - --confirm -- Skips the delay before installation + --confirm -- Skips the delay before installation --uv-install-dir={path} -- The directory to install \`uv\` into --qmk-distrib-dir={path} -- The directory to install the QMK distribution into --skip-clean -- Skip cleaning the QMK distribution directory From a8d4e89e41f3bd795e72149bbde8b360c59e9338 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 2 Apr 2025 17:08:14 +1100 Subject: [PATCH 13/40] Allow relocation of `uv` tools directory, defaulting to inside the MSYS tree on Windows. --- util/env-bootstrap.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 309606886f2..1e6557df532 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -9,6 +9,7 @@ # CONFIRM: Skip the pre-install delay. (or: --confirm) # QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...) # UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...) +# UV_TOOL_DIR: The directory to install `uv` tools to. (or: --uv-tool-dir=...) # SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean) # SKIP_PACKAGE_MANAGER: Skip installing the necessary packages for the package manager. (or: --skip-package-manager) # SKIP_UV: Skip installing `uv`. (or: --skip-uv) @@ -45,6 +46,7 @@ --help -- Shows this help text --confirm -- Skips the delay before installation --uv-install-dir={path} -- The directory to install \`uv\` into + --uv-tool-dir={path} -- The directory to install \`uv\` tools into --qmk-distrib-dir={path} -- The directory to install the QMK distribution into --skip-clean -- Skip cleaning the QMK distribution directory --skip-package-manager -- Skip installing the necessary packages for the package manager @@ -355,9 +357,10 @@ __EOT__ export TMPDIR="$(cygpath -w "$TMP")" export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} + export UV_TOOL_DIR=${UV_TOOL_DIR:-"$QMK_DISTRIB_DIR/tools"} fi - # Work out where we want to install the distribution + # Work out where we want to install the distribution and tools export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} script_parse_args "$@" @@ -367,7 +370,6 @@ __EOT__ [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv setup_paths - [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli # Clear out the distrib directory if necessary if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then @@ -378,6 +380,7 @@ __EOT__ fi mkdir -p "$QMK_DISTRIB_DIR" + [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools clean_tarballs From 10faf16edf9e1d672f597179ff6fb098a4e4a41e Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 4 Apr 2025 22:01:11 +1100 Subject: [PATCH 14/40] Potentially uninitialised variable. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 1e6557df532..b9137f2d08b 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -106,7 +106,7 @@ __EOT__ } nsudo() { - [[ ${EUID} -ne 0 ]] && echo "sudo" + [[ ${EUID:-} -ne 0 ]] && echo "sudo" true } From 1fb6323a0d5c8e8f15e336415cfab01508e6a5d1 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 21 Apr 2025 11:32:01 +1000 Subject: [PATCH 15/40] macOS path fix, `dos2unix` dependency. --- util/env-bootstrap.sh | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index b9137f2d08b..a3a955e3d1b 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -35,7 +35,6 @@ ################################################################################ { # this ensures the entire script is downloaded # - set -eu # Work out which `sed` to use @@ -116,10 +115,10 @@ __EOT__ local filename=${2:-$(basename "$url")} local quiet='' if [ -n "$(command -v curl 2>/dev/null || true)" ]; then - [ $filename = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 + [ "$filename" = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2 curl -LSf $quiet -o "$filename" "$url" elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then - [ $filename = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 + [ "$filename" = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2 wget $quiet "-O$filename" "$url" else echo "Please install 'curl' or 'wget' to continue." >&2 @@ -182,10 +181,10 @@ __EOT__ macos) if [ -n "$(command -v brew 2>/dev/null || true)" ]; then echo "It will also install the following system packages using 'brew':" >&2 - echo " zstd clang-format make hidapi libusb" >&2 + echo " zstd clang-format make hidapi libusb dos2unix" >&2 preinstall_delay || exit 1 brew update && brew upgrade --formulae - brew install zstd clang-format make hidapi libusb + brew install zstd clang-format make hidapi libusb dos2unix else echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 exit 1 @@ -195,33 +194,33 @@ __EOT__ case $(grep ID /etc/os-release) in *arch* | *manjaro*) echo "It will also install the following system packages using 'pacman':" >&2 - echo " zstd base-devel clang diffutils unzip wget zip hidapi" >&2 + echo " zstd base-devel clang diffutils unzip wget zip hidapi dos2unix" >&2 preinstall_delay || exit 1 - $(nsudo) pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip + $(nsudo) pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip dos2unix $(nsudo) pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled ;; *debian* | *ubuntu*) echo "It will also install the following system packages using 'apt':" >&2 - echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0" >&2 + echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix" >&2 preinstall_delay || exit 1 $(nsudo) apt-get update DEBIAN_FRONTEND=noninteractive \ - $(nsudo) apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 + $(nsudo) apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix ;; *fedora*) echo "It will also install the following system packages using 'dnf':" >&2 - echo " zstd clang diffutils gcc git unzip wget zip hidapi" >&2 + echo " zstd clang diffutils gcc git unzip wget zip hidapi dos2unix" >&2 echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 preinstall_delay || exit 1 - $(nsudo) dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable + $(nsudo) dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable ;; *gentoo*) echo "It will also the following packages using 'emerge':" >&2 - echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi" >&2 + echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix" >&2 preinstall_delay || exit 1 $(nsudo) emerge -au --noreplace \ - app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi + app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix ;; *slackware*) echo "It will also the following packages using 'sboinstall':" >&2 @@ -231,19 +230,19 @@ __EOT__ ;; *solus*) echo "It will also install the following system packages using 'eopkg':" >&2 - echo " system.devel zstd git wget zip unzip python3" >&2 + echo " system.devel zstd git wget zip unzip python3 dos2unix" >&2 preinstall_delay || exit 1 $(nsudo) eopkg -y update-repo $(nsudo) eopkg -y upgrade $(nsudo) eopkg -y install \ - -c system.devel zstd git wget zip unzip python3 + -c system.devel zstd git wget zip unzip python3 dos2unix ;; *void*) echo "It will also the following packages using 'xbps-install':" >&2 - echo " zstd git make wget unzip zip python3" >&2 + echo " zstd git make wget unzip zip python3 dos2unix" >&2 preinstall_delay || exit 1 $(nsudo) xbps-install -y \ - zstd git make wget unzip zip python3 + zstd git make wget unzip zip python3 dos2unix ;; *) echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" @@ -278,7 +277,7 @@ __EOT__ install_qmk_cli() { # Install the QMK CLI - uv tool install --force --with pip --upgrade --python 3.13 qmk + uv tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk # QMK is installed to... local qmk_tooldir="$(uv tool dir)/qmk" @@ -351,6 +350,9 @@ __EOT__ rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true } + # Set the Python version we want to use with the QMK CLI + export PYTHON_TARGET_VERSION=3.13 + # Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. # Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then @@ -361,7 +363,7 @@ __EOT__ fi # Work out where we want to install the distribution and tools - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$HOME/.local/share/qmk} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)} script_parse_args "$@" From d10d6377620d7a282df4e6bf2955b9c87344d533 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 21 Apr 2025 17:26:46 +1000 Subject: [PATCH 16/40] Hard-coded default distrib directory. --- util/env-bootstrap.sh | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index a3a955e3d1b..529e794a6ea 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -316,12 +316,12 @@ __EOT__ fi # Download the toolchain release to the toolchains location - echo "Downloading compiler toolchain..." >&2 + echo "Downloading compiler toolchains..." >&2 local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")" download_url "$toolchain_url" "$target_file" # Extract the toolchain - echo "Extracting compiler toolchain to '$QMK_DISTRIB_DIR'..." >&2 + echo "Extracting compiler toolchains to '$QMK_DISTRIB_DIR'..." >&2 tar xf "$target_file" -C "$QMK_DISTRIB_DIR" --strip-components=1 } @@ -350,6 +350,23 @@ __EOT__ rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true } + default_distrib_dir() { + case $(fn_os) in + macos) + echo "$HOME/Library/Application Support/qmk" + ;; + linux) + echo "$HOME/.local/share/qmk" + ;; + windows) + echo "/opt/qmk" + ;; + *) + echo "No default installation directory for this OS." >&2 + exit 1 + esac + } + # Set the Python version we want to use with the QMK CLI export PYTHON_TARGET_VERSION=3.13 @@ -358,16 +375,16 @@ __EOT__ if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then export TMPDIR="$(cygpath -w "$TMP")" export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} export UV_TOOL_DIR=${UV_TOOL_DIR:-"$QMK_DISTRIB_DIR/tools"} fi # Work out where we want to install the distribution and tools - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(default_distrib_dir)} script_parse_args "$@" - echo "This script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, and the QMK CLI, toolchains, and flashing utilities to ${QMK_DISTRIB_DIR}." + echo "This QMK CLI installation script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, the QMK CLI to the \`uv\` tools" + echo "directory, as well as toolchains and flashing utilities to '${QMK_DISTRIB_DIR}'." [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv From 0d38d2adcf7041dd8a3bec106b74e8de0a7f22ac Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 21 Apr 2025 23:22:57 +1000 Subject: [PATCH 17/40] Fixup chicken-and-egg problem with `uv` installation. --- util/env-bootstrap.sh | 141 ++++++++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 529e794a6ea..c76e9e8408b 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -109,7 +109,6 @@ __EOT__ true } - download_url() { local url=$1 local filename=${2:-$(basename "$url")} @@ -175,16 +174,68 @@ __EOT__ sleep 10 } + get_package_manager_deps() { + case $(fn_os) in + macos) + echo "zstd clang-format make hidapi libusb dos2unix" + ;; + linux) + case $(grep ID /etc/os-release) in + *arch* | *manjaro*) + echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix" + ;; + *debian* | *ubuntu*) + echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix" + ;; + *fedora*) + echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel" + ;; + *gentoo*) + echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix" + ;; + *slackware*) + echo "python3" + ;; + *solus*) + echo "system.devel zstd git wget zip unzip python3 dos2unix" + ;; + *void*) + echo "zstd git make wget unzip zip python3 dos2unix" + ;; + *) + echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" >&2 + echo >&2 + echo "https://docs.qmk.fm/#/getting_started_docker" >&2 + exit 1 + ;; + esac + ;; + windows) + echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x" + ;; + *) + echo "Sorry, we don't recognize your OS. Try using a compatible OS instead:" >&2 + echo >&2 + echo "https://docs.qmk.fm/newbs_getting_started#set-up-your-environment" >&2 + exit 1 + ;; + esac + } + + print_package_manager_deps_and_delay() { + get_package_manager_deps | tr ' ' '\n' | sort | xargs -I'{}' echo " - {}" >&2 + preinstall_delay || exit 1 + } + install_package_manager_deps() { # Install the necessary packages for the package manager case $(fn_os) in macos) if [ -n "$(command -v brew 2>/dev/null || true)" ]; then echo "It will also install the following system packages using 'brew':" >&2 - echo " zstd clang-format make hidapi libusb dos2unix" >&2 - preinstall_delay || exit 1 + print_package_manager_deps_and_delay brew update && brew upgrade --formulae - brew install zstd clang-format make hidapi libusb dos2unix + brew install $(get_package_manager_deps) else echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2 exit 1 @@ -194,55 +245,42 @@ __EOT__ case $(grep ID /etc/os-release) in *arch* | *manjaro*) echo "It will also install the following system packages using 'pacman':" >&2 - echo " zstd base-devel clang diffutils unzip wget zip hidapi dos2unix" >&2 - preinstall_delay || exit 1 - $(nsudo) pacman --needed --noconfirm -S zstd base-devel clang diffutils unzip wget zip dos2unix - $(nsudo) pacman --needed --noconfirm -S hidapi || true # This will fail if the community repo isn't enabled + print_package_manager_deps_and_delay + $(nsudo) pacman --needed --noconfirm -S $(get_package_manager_deps) ;; *debian* | *ubuntu*) echo "It will also install the following system packages using 'apt':" >&2 - echo " zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix" >&2 - preinstall_delay || exit 1 + print_package_manager_deps_and_delay $(nsudo) apt-get update DEBIAN_FRONTEND=noninteractive \ - $(nsudo) apt-get --quiet --yes install zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix + $(nsudo) apt-get --quiet --yes $(get_package_manager_deps) ;; *fedora*) echo "It will also install the following system packages using 'dnf':" >&2 - echo " zstd clang diffutils gcc git unzip wget zip hidapi dos2unix" >&2 - echo "And whichever of the following is available, depending on which packages are provided by the distro:" >&2 - echo " libusb-devel, libusb1-devel, libusb-compat-0.1-devel, or libusb0-devel" >&2 - preinstall_delay || exit 1 - $(nsudo) dnf -y install zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel --skip-unavailable + print_package_manager_deps_and_delay + $(nsudo) dnf -y install $(get_package_manager_deps) --skip-unavailable ;; *gentoo*) echo "It will also the following packages using 'emerge':" >&2 - echo " app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix" >&2 - preinstall_delay || exit 1 - $(nsudo) emerge -au --noreplace \ - app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix + print_package_manager_deps_and_delay + $(nsudo) emerge -au --noreplace $(get_package_manager_deps) ;; *slackware*) echo "It will also the following packages using 'sboinstall':" >&2 - echo " python3" >&2 - preinstall_delay || exit 1 - $(nsudo) sboinstall python3 # Rest tbd? + print_package_manager_deps_and_delay + $(nsudo) sboinstall $(get_package_manager_deps) ;; *solus*) echo "It will also install the following system packages using 'eopkg':" >&2 - echo " system.devel zstd git wget zip unzip python3 dos2unix" >&2 - preinstall_delay || exit 1 + print_package_manager_deps_and_delay $(nsudo) eopkg -y update-repo $(nsudo) eopkg -y upgrade - $(nsudo) eopkg -y install \ - -c system.devel zstd git wget zip unzip python3 dos2unix + $(nsudo) eopkg -y install $(get_package_manager_deps) ;; *void*) echo "It will also the following packages using 'xbps-install':" >&2 - echo " zstd git make wget unzip zip python3 dos2unix" >&2 - preinstall_delay || exit 1 - $(nsudo) xbps-install -y \ - zstd git make wget unzip zip python3 dos2unix + print_package_manager_deps_and_delay + $(nsudo) xbps-install -y $(get_package_manager_deps) ;; *) echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" @@ -252,6 +290,12 @@ __EOT__ ;; esac ;; + windows) + echo "It will also install the following packages using 'pacman'/'pacboy':" >&2 + print_package_manager_deps_and_delay + $(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys + $(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps) + ;; esac } @@ -350,23 +394,6 @@ __EOT__ rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true } - default_distrib_dir() { - case $(fn_os) in - macos) - echo "$HOME/Library/Application Support/qmk" - ;; - linux) - echo "$HOME/.local/share/qmk" - ;; - windows) - echo "/opt/qmk" - ;; - *) - echo "No default installation directory for this OS." >&2 - exit 1 - esac - } - # Set the Python version we want to use with the QMK CLI export PYTHON_TARGET_VERSION=3.13 @@ -375,19 +402,22 @@ __EOT__ if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then export TMPDIR="$(cygpath -w "$TMP")" export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} export UV_TOOL_DIR=${UV_TOOL_DIR:-"$QMK_DISTRIB_DIR/tools"} fi - # Work out where we want to install the distribution and tools - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(default_distrib_dir)} - script_parse_args "$@" - echo "This QMK CLI installation script will install \`uv\` to ${UV_INSTALL_DIR:-the default location}, the QMK CLI to the \`uv\` tools" - echo "directory, as well as toolchains and flashing utilities to '${QMK_DISTRIB_DIR}'." + echo "This QMK CLI installation script will install \`uv\`, the QMK CLI to the \`uv\` tools" + echo "directory, as well as toolchains and flashing utilities." [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv + + # Work out where we want to install the distribution and tools now that `uv` is installed + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --with platformdirs -)} + + # Make sure the usual `uv` and other associated directories are on the $PATH setup_paths # Clear out the distrib directory if necessary @@ -407,6 +437,11 @@ __EOT__ # Notify the user that they may need to restart their shell to get the `qmk` command hash -r echo + echo "QMK CLI installation complete." + echo "The QMK CLI has been installed to '$(dirname "$(command -v qmk)")'." + echo "The QMK CLI venv has been created at '$(uv tool dir)/qmk'." + echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." + echo echo "You may need to restart your shell to gain access to the 'qmk' command." echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" From fa1e51dd1940b24d6d8f6cc5eb55bb6df6ad5989 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 21 Apr 2025 23:38:33 +1000 Subject: [PATCH 18/40] Python version. --- util/env-bootstrap.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index c76e9e8408b..7a4189a69bb 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -408,14 +408,13 @@ __EOT__ script_parse_args "$@" - echo "This QMK CLI installation script will install \`uv\`, the QMK CLI to the \`uv\` tools" - echo "directory, as well as toolchains and flashing utilities." + echo "This QMK CLI installation script will install \`uv\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities." [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv # Work out where we want to install the distribution and tools now that `uv` is installed - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --with platformdirs -)} + export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)} # Make sure the usual `uv` and other associated directories are on the $PATH setup_paths From 7c7a9e0e34d24139eaeabcdc6b727239ff67e7fb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 00:25:47 +1000 Subject: [PATCH 19/40] I suppose we need `git` too. --- util/env-bootstrap.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 7a4189a69bb..e9ab838175d 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -177,30 +177,30 @@ __EOT__ get_package_manager_deps() { case $(fn_os) in macos) - echo "zstd clang-format make hidapi libusb dos2unix" + echo "zstd clang-format make hidapi libusb dos2unix git" ;; linux) case $(grep ID /etc/os-release) in *arch* | *manjaro*) - echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix" + echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; *debian* | *ubuntu*) - echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix" + echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix git" ;; *fedora*) - echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel" + echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git" ;; *gentoo*) - echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix" + echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix dev-vcs/git" ;; *slackware*) echo "python3" ;; *solus*) - echo "system.devel zstd git wget zip unzip python3 dos2unix" + echo "system.devel zstd git wget zip unzip python3 dos2unix git" ;; *void*) - echo "zstd git make wget unzip zip python3 dos2unix" + echo "zstd git make wget unzip zip python3 dos2unix git" ;; *) echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" >&2 @@ -211,7 +211,7 @@ __EOT__ esac ;; windows) - echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x" + echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x git:x" ;; *) echo "Sorry, we don't recognize your OS. Try using a compatible OS instead:" >&2 From c57add534ee9e565309588386d386688ce55b52a Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 00:38:06 +1000 Subject: [PATCH 20/40] Apple diff --- lib/python/qmk/cli/doctor/check.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/doctor/check.py b/lib/python/qmk/cli/doctor/check.py index 2da9835efef..89a69302440 100644 --- a/lib/python/qmk/cli/doctor/check.py +++ b/lib/python/qmk/cli/doctor/check.py @@ -67,7 +67,10 @@ def _check_dos2unix_version(): def _check_diff_version(): last_line = ESSENTIAL_BINARIES['diff']['output'].split('\n')[0] - version_number = last_line.split()[3] + if 'Apple diff' in last_line: + version_number = last_line + else: + version_number = last_line.split()[3] cli.log.info('Found diff version %s', version_number) return CheckStatus.OK From 36c2db8629c126c0c3cd274fa3633fab29122062 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 00:44:10 +1000 Subject: [PATCH 21/40] Cleanup. --- util/env-bootstrap.sh | 48 ++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index e9ab838175d..fd63aa33374 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -176,32 +176,17 @@ __EOT__ get_package_manager_deps() { case $(fn_os) in - macos) - echo "zstd clang-format make hidapi libusb dos2unix git" - ;; + macos) echo "zstd clang-format make hidapi libusb dos2unix git" ;; + windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x git:x" ;; linux) case $(grep ID /etc/os-release) in - *arch* | *manjaro*) - echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" - ;; - *debian* | *ubuntu*) - echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix git" - ;; - *fedora*) - echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git" - ;; - *gentoo*) - echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix dev-vcs/git" - ;; - *slackware*) - echo "python3" - ;; - *solus*) - echo "system.devel zstd git wget zip unzip python3 dos2unix git" - ;; - *void*) - echo "zstd git make wget unzip zip python3 dos2unix git" - ;; + *arch* | *manjaro*) echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; + *debian* | *ubuntu*) echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix git" ;; + *fedora*) echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git" ;; + *gentoo*) echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix dev-vcs/git" ;; + *slackware*) echo "python3" ;; + *solus*) echo "system.devel zstd git wget zip unzip python3 dos2unix git" ;; + *void*) echo "zstd git make wget unzip zip python3 dos2unix git" ;; *) echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" >&2 echo >&2 @@ -210,9 +195,6 @@ __EOT__ ;; esac ;; - windows) - echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x git:x" - ;; *) echo "Sorry, we don't recognize your OS. Try using a compatible OS instead:" >&2 echo >&2 @@ -241,6 +223,12 @@ __EOT__ exit 1 fi ;; + windows) + echo "It will also install the following packages using 'pacman'/'pacboy':" >&2 + print_package_manager_deps_and_delay + $(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys + $(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps) + ;; linux) case $(grep ID /etc/os-release) in *arch* | *manjaro*) @@ -290,12 +278,6 @@ __EOT__ ;; esac ;; - windows) - echo "It will also install the following packages using 'pacman'/'pacboy':" >&2 - print_package_manager_deps_and_delay - $(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys - $(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps) - ;; esac } From a171b36eafc40204726a7f3edbea4f31a582b04f Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 00:49:32 +1000 Subject: [PATCH 22/40] stderr. --- util/env-bootstrap.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index fd63aa33374..ec336709d6b 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -271,9 +271,9 @@ __EOT__ $(nsudo) xbps-install -y $(get_package_manager_deps) ;; *) - echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" - echo - echo "https://docs.qmk.fm/#/getting_started_docker" + echo "Sorry, we don't recognize your distribution. Try using the docker image instead:" >&2 + echo >&2 + echo "https://docs.qmk.fm/#/getting_started_docker" >&2 exit 1 ;; esac @@ -390,7 +390,7 @@ __EOT__ script_parse_args "$@" - echo "This QMK CLI installation script will install \`uv\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities." + echo "This QMK CLI installation script will install \`uv\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities." >&2 [ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; } [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv @@ -417,14 +417,14 @@ __EOT__ # Notify the user that they may need to restart their shell to get the `qmk` command hash -r - echo - echo "QMK CLI installation complete." - echo "The QMK CLI has been installed to '$(dirname "$(command -v qmk)")'." - echo "The QMK CLI venv has been created at '$(uv tool dir)/qmk'." - echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." - echo - echo "You may need to restart your shell to gain access to the 'qmk' command." - echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" - echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" + echo >&2 + echo "QMK CLI installation complete." >&2 + echo "The QMK CLI has been installed to '$(dirname "$(command -v qmk)")'." >&2 + echo "The QMK CLI venv has been created at '$(uv tool dir)/qmk'." >&2 + echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." >&2 + echo >&2 + echo "You may need to restart your shell to gain access to the 'qmk' command." >&2 + echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" >&2 + echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" >&2 } # this ensures the entire script is downloaded # From de8d66b6fd826a411369f7cbcec9e00040e16ad6 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 09:19:07 +1000 Subject: [PATCH 23/40] No need for sudo under QMK MSYS --- util/env-bootstrap.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index ec336709d6b..a1c3d6c24d2 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -105,7 +105,12 @@ __EOT__ } nsudo() { - [[ ${EUID:-} -ne 0 ]] && echo "sudo" + if [ "$(fn_os)" = "windows" ]; then + # No need for sudo under QMK MSYS + return + elif [ ${EUID:-} -ne 0 ]; then + echo "sudo" + fi true } From decf39592e367f2e945a63f0dcc69c0287d62612 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 09:25:57 +1000 Subject: [PATCH 24/40] Package naming on QMK MSYS. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index a1c3d6c24d2..06ec2993d58 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -182,7 +182,7 @@ __EOT__ get_package_manager_deps() { case $(fn_os) in macos) echo "zstd clang-format make hidapi libusb dos2unix git" ;; - windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix:x git:x" ;; + windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix: git:" ;; linux) case $(grep ID /etc/os-release) in *arch* | *manjaro*) echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; From b998dcfd0efb57cf01f9ff0c1d9d8401109d4358 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 09:43:39 +1000 Subject: [PATCH 25/40] More Windows deps. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 06ec2993d58..07594f8ce61 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -182,7 +182,7 @@ __EOT__ get_package_manager_deps() { case $(fn_os) in macos) echo "zstd clang-format make hidapi libusb dos2unix git" ;; - windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix: git:" ;; + windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix: git: unzip:" ;; linux) case $(grep ID /etc/os-release) in *arch* | *manjaro*) echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; From 77366af7ec863ed476a075ae15880cdc794d470b Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 09:51:20 +1000 Subject: [PATCH 26/40] `uv` tools directory move. --- lib/python/qmk/cli/__init__.py | 2 +- util/env-bootstrap.sh | 58 +++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 8ce14b4ee7b..7a7b017d829 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -15,7 +15,7 @@ from milc import cli, __VERSION__ from milc.questions import yesno # Ensure the QMK distribution is on the `$PATH` if present. -_default_distrib_path = '/opt/qmk' if 'windows' in platform.platform().lower() else platformdirs.user_data_dir('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`! +_default_distrib_path = cli.run(['cygpath', '-w', '/opt/qmk']).stdout.strip() if 'windows' in platform.platform().lower() else platformdirs.user_data_dir('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`! QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _default_distrib_path)) if QMK_DISTRIB_DIR.exists(): os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH'] diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 07594f8ce61..4a9e0072f6a 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -288,7 +288,7 @@ __EOT__ install_uv() { # Install `uv` (or update as necessary) - download_url https://astral.sh/uv/install.sh - | TMPDIR=${TMPDIR:-} UV_INSTALL_DIR=${UV_INSTALL_DIR:-} sh + download_url https://astral.sh/uv/install.sh - | TMPDIR="$(windows_ish_path "${TMPDIR:-}")" UV_INSTALL_DIR="$(windows_ish_path "${UV_INSTALL_DIR:-}")" sh } setup_paths() { @@ -304,19 +304,28 @@ __EOT__ if [ -n "${UV_INSTALL_DIR:-}" ]; then export PATH="$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH" # cater for both "flat" and "hierarchical" installs of `uv` fi + + if [ -n "${UV_TOOL_BIN_DIR:-}" ]; then + export PATH="$UV_TOOL_BIN_DIR:$PATH" + fi + } + + uv_command() { + if [ "$(fn_os)" = "windows" ]; then + UV_TOOL_DIR="$(windows_ish_path "${UV_TOOL_DIR:-}")" \ + UV_TOOL_BIN_DIR="$(windows_ish_path "${UV_TOOL_BIN_DIR:-}")" \ + uv "$@" + else + uv "$@" + fi } install_qmk_cli() { # Install the QMK CLI - uv tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk + uv_command tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk # QMK is installed to... - local qmk_tooldir="$(uv tool dir)/qmk" - - # Convert it to a unix-style path if we're on Windows/Msys2 - if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - qmk_tooldir="$(cygpath -u "$qmk_tooldir")" - fi + local qmk_tooldir="$(posix_ish_path "$(uv_command tool dir)/qmk")" # Activate the environment if [ -e "$qmk_tooldir/bin" ]; then @@ -329,8 +338,8 @@ __EOT__ fi # Install the QMK dependencies - uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt - uv pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt + uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt + uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt # Deactivate the environment deactivate @@ -381,16 +390,27 @@ __EOT__ rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true } + windows_ish_path() { + [ -n "$1" ] || return 0 + [ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -w "$1" || echo "$1" + } + + posix_ish_path() { + [ -n "$1" ] || return 0 + [ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -u "$1" || echo "$1" + } + # Set the Python version we want to use with the QMK CLI export PYTHON_TARGET_VERSION=3.13 # Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory. # Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS. if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then - export TMPDIR="$(cygpath -w "$TMP")" - export UV_INSTALL_DIR=${UV_INSTALL_DIR:-/opt/uv} - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-/opt/qmk} - export UV_TOOL_DIR=${UV_TOOL_DIR:-"$QMK_DISTRIB_DIR/tools"} + export TMPDIR="$(posix_ish_path "$TMP")" + export UV_INSTALL_DIR="$(posix_ish_path "${UV_INSTALL_DIR:-/opt/uv}")" + export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-/opt/qmk}")" + export UV_TOOL_DIR="$(posix_ish_path "${UV_TOOL_DIR:-"$UV_INSTALL_DIR/tools"}")" + export UV_TOOL_BIN_DIR="$(posix_ish_path "$UV_TOOL_DIR/bin")" fi script_parse_args "$@" @@ -401,7 +421,7 @@ __EOT__ [ -n "${SKIP_UV:-}" ] || install_uv # Work out where we want to install the distribution and tools now that `uv` is installed - export QMK_DISTRIB_DIR=${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)} + 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 -)}")" # Make sure the usual `uv` and other associated directories are on the $PATH setup_paths @@ -424,12 +444,12 @@ __EOT__ hash -r echo >&2 echo "QMK CLI installation complete." >&2 - echo "The QMK CLI has been installed to '$(dirname "$(command -v qmk)")'." >&2 - echo "The QMK CLI venv has been created at '$(uv tool dir)/qmk'." >&2 + echo "The QMK CLI has been installed to '$(posix_ish_path "$(dirname "$(command -v qmk)")")'." >&2 + echo "The QMK CLI venv has been created at '$(posix_ish_path "$(uv_command tool dir)/qmk")'." >&2 echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." >&2 echo >&2 echo "You may need to restart your shell to gain access to the 'qmk' command." >&2 - echo "Alternatively, add "$(dirname "$(command -v qmk)")" to your \$PATH:" >&2 - echo " export PATH=\"$(dirname "$(command -v qmk)"):\$PATH\"" >&2 + echo "Alternatively, add "$(posix_ish_path "$(dirname "$(command -v qmk)")")" to your \$PATH:" >&2 + echo " export PATH=\"$(posix_ish_path "$(dirname "$(command -v qmk)")"):\$PATH\"" >&2 } # this ensures the entire script is downloaded # From e52491a71f7ef43649c3117404cab05d0de0f5e5 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 22:27:30 +1000 Subject: [PATCH 27/40] Attempt at installing Windows drivers. --- util/env-bootstrap.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 4a9e0072f6a..3d53df909ce 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -53,6 +53,7 @@ --skip-qmk-cli -- Skip installing the QMK CLI --skip-qmk-toolchains -- Skip installing the QMK toolchains --skip-qmk-flashutils -- Skip installing the QMK flashing utilities + --skip-windows-drivers -- Skip installing the Windows drivers for the flashing utilities __EOT__ } @@ -385,6 +386,29 @@ __EOT__ tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" } + install_windows_drivers() { + # Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer + local latest_driver_installer_release=$(download_url https://api.github.com/repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') + # Download the specific release asset + local driver_installer_url=$(download_url https://api.github.com/repos/qmk/qmk_driver_installer/releases/tags/$latest_driver_installer_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep '\.exe') + if [ -z "$driver_installer_url" ]; then + echo "No driver installer found." >&2 + exit 1 + fi + # Download the driver installer release to the toolchains location + echo "Downloading driver installer..." >&2 + local target_file="$QMK_DISTRIB_DIR/$(basename "$driver_installer_url")" + download_url "$driver_installer_url" "$target_file" + # Download the drivers list + download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/drivers.txt" "$QMK_DISTRIB_DIR/drivers.txt" + # Execute the driver installer + cd "$QMK_DISTRIB_DIR" + cmd.exe //c "qmk_driver_installer.exe --all --force drivers.txt" + cd - + # Remove the temporary files + rm -f "$QMK_DISTRIB_DIR/qmk_driver_installer.exe" "$QMK_DISTRIB_DIR/drivers.txt" || true + } + clean_tarballs() { # Clean up the tarballs rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true @@ -438,6 +462,9 @@ __EOT__ [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools + if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then + [ -n "${SKIP_WINDOWS_DRIVERS:-}" ] || install_windows_drivers + fi clean_tarballs # Notify the user that they may need to restart their shell to get the `qmk` command From 176aa1f7dba151dd43c073fb15d29ee7b0aec4a3 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 22:32:02 +1000 Subject: [PATCH 28/40] Docs, first pass. --- docs/driver_installation_zadig.md | 2 +- docs/faq_build.md | 4 +- docs/newbs_getting_started.md | 66 ++++++++++--------------------- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/docs/driver_installation_zadig.md b/docs/driver_installation_zadig.md index 1a5bd1cc346..78b631e6469 100644 --- a/docs/driver_installation_zadig.md +++ b/docs/driver_installation_zadig.md @@ -4,7 +4,7 @@ QMK presents itself to the host as a regular HID keyboard device, and as such re There are two notable exceptions: the Caterina bootloader, usually seen on Pro Micros, and the HalfKay bootloader shipped with PJRC Teensys, appear as a serial port and a generic HID device respectively, and so do not require a driver. -We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the `qmk_install.sh` script will have already installed the drivers for you. +We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the QMK CLI instalation script will have already installed the drivers for you. ## Installation diff --git a/docs/faq_build.md b/docs/faq_build.md index 54ed576c708..91831296b03 100644 --- a/docs/faq_build.md +++ b/docs/faq_build.md @@ -13,7 +13,7 @@ An example of using `sudo`, when your controller is ATMega32u4: or just: - $ sudo make ::flash + $ qmk flash -kb -km Note that running `make` with `sudo` is generally ***not*** a good idea, and you should use one of the former methods, if possible. @@ -44,7 +44,7 @@ Pro Micro (Atmega32u4), make sure to include `CONFIG_USB_ACM=y`. Other devices m Issues encountered when flashing keyboards on Windows are most often due to having the wrong drivers installed for the bootloader, or none at all. -Re-running the QMK installation script (`./util/qmk_install.sh` from the `qmk_firmware` directory in MSYS2 or WSL) or reinstalling the QMK Toolbox may fix the issue. Alternatively, you can download and run the [`qmk_driver_installer`](https://github.com/qmk/qmk_driver_installer) package manually. +Re-running the QMK installation script (`curl -fsSL https://install.qmk.fm | sh`) or reinstalling the QMK Toolbox may fix the issue. Alternatively, you can download and run the [`qmk_driver_installer`](https://github.com/qmk/qmk_driver_installer) package manually. If that doesn't work, then you may need to download and run Zadig. See [Bootloader Driver Installation with Zadig](driver_installation_zadig) for more detailed information. diff --git a/docs/newbs_getting_started.md b/docs/newbs_getting_started.md index 9ebcccc77f2..58407ca8fee 100644 --- a/docs/newbs_getting_started.md +++ b/docs/newbs_getting_started.md @@ -50,84 +50,60 @@ You will need to install [MSYS2](https://www.msys2.org). Once installed, close a Install the QMK CLI by running: ```sh -pacman --needed --noconfirm --disable-download-timeout -S git mingw-w64-x86_64-python-qmk +curl -fsSL https://install.qmk.fm | sh ``` :::: ==== macOS -QMK maintains a Homebrew tap and formula which will automatically install the CLI and all necessary dependencies. - #### Prerequisites You will need to install Homebrew. Follow the instructions on https://brew.sh. -::: tip -If you are using an Apple Silicon machine, the installation process will take significantly longer because GitHub actions do not have native runners to build binary packages for the ARM and AVR toolchains. -::: - #### Installation Install the QMK CLI by running: ```sh -brew install qmk/qmk/qmk +curl -fsSL https://install.qmk.fm | sh ``` ==== Linux/WSL +#### Installation + +::: info +Many Linux distributions are supported, but not all. Mainstream distributions will have best success -- if possible, choose either Debian or its derivatives (such as Ubuntu, or Mint), CentOS or its derivatives (such as Fedora, or Rocky Linux), and Arch or its derivatives (such as Manjaro, or CachyOS). +::: + +Install the QMK CLI by running: + +```sh +curl -fsSL https://install.qmk.fm | sh +``` + ::: tip **Note for WSL users**: By default, the installation process will clone the QMK repository into your WSL home directory, but if you have cloned manually, ensure that it is located inside the WSL instance instead of the Windows filesystem (ie. not in `/mnt`), as accessing it is currently [extremely slow](https://github.com/microsoft/WSL/issues/4197). ::: -#### Prerequisites - -You will need to install Git and Python. It's very likely that you already have both, but if not, one of the following commands should install them: - -* Debian / Ubuntu / Devuan: `sudo apt install -y git python3-pip` -* Fedora / Red Hat / CentOS: `sudo yum -y install git python3-pip` -* Arch / Manjaro: `sudo pacman --needed --noconfirm -S git python-pip libffi` -* Void: `sudo xbps-install -y git python3-pip` -* Solus: `sudo eopkg -y install git python3` -* Sabayon: `sudo equo install dev-vcs/git dev-python/pip` -* Gentoo: `sudo emerge dev-vcs/git dev-python/pip` - -#### Installation - -Install the QMK CLI by running: - -```sh -python3 -m pip install --user qmk -``` - -#### Community Packages - -These packages are maintained by community members, so may not be up to date or completely functional. If you encounter problems, please report them to their respective maintainers. - -On Arch-based distros you can install the CLI from the official repositories (NOTE: at the time of writing this package marks some dependencies as optional that should not be): - -```sh -sudo pacman -S qmk -``` - -You can also try the `qmk-git` package from AUR: - -```sh -yay -S qmk-git -``` - ==== FreeBSD #### Installation +::: warning +FreeBSD support is provided on a best-effort basis by the community instead of the QMK maintainers. It is strongly suggested that you use either Windows, macOS, or a supported distribution of Linux instead. +::: + Install the FreeBSD package for QMK CLI by running: ```sh pkg install -g "py*-qmk" ``` -NOTE: remember to follow the instructions printed at the end of installation (use `pkg info -Dg "py*-qmk"` to show them again). +::: info NOTE +Remember to follow the instructions printed at the end of installation (use `pkg info -Dg "py*-qmk"` to show them again). +::: ::::: From 454767e758f0b73d277d09cd84433c3321fd5111 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 22 Apr 2025 22:41:26 +1000 Subject: [PATCH 29/40] Script args comments. --- util/env-bootstrap.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 3d53df909ce..3d831616938 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -16,6 +16,7 @@ # SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli) # SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains) # SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils) +# SKIP_WINDOWS_DRIVERS: Skip installing the Windows drivers for the flashing utilities. (or: --skip-windows-drivers) # # Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`). ################################################################################ From c80de06b66e46af182fdb0e9fbeaf8591cd793b9 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 13 May 2025 10:02:19 +1000 Subject: [PATCH 30/40] Oops. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 3d831616938..d10e99921de 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -248,7 +248,7 @@ __EOT__ print_package_manager_deps_and_delay $(nsudo) apt-get update DEBIAN_FRONTEND=noninteractive \ - $(nsudo) apt-get --quiet --yes $(get_package_manager_deps) + $(nsudo) apt-get --quiet --yes install $(get_package_manager_deps) ;; *fedora*) echo "It will also install the following system packages using 'dnf':" >&2 From ef67e898362b192cfa71ce246261a1ce2798fe66 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 13 May 2025 10:08:19 +1000 Subject: [PATCH 31/40] Oops. --- util/env-bootstrap.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index d10e99921de..fde1a955f4a 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -110,7 +110,7 @@ __EOT__ if [ "$(fn_os)" = "windows" ]; then # No need for sudo under QMK MSYS return - elif [ ${EUID:-} -ne 0 ]; then + elif [ $(id -u) -ne 0 ]; then echo "sudo" fi true @@ -445,12 +445,12 @@ __EOT__ [ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps [ -n "${SKIP_UV:-}" ] || install_uv - # Work out where we want to install the distribution and tools now that `uv` is installed - 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 -)}")" - # Make sure the usual `uv` and other associated directories are on the $PATH setup_paths + # Work out where we want to install the distribution and tools now that `uv` is installed + 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 -)}")" + # Clear out the distrib directory if necessary if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then if [ -d "$QMK_DISTRIB_DIR" ]; then From 6d481f176dc7b604404cf1b82f6bbb391b3354af Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 22 May 2025 19:40:45 +1000 Subject: [PATCH 32/40] Add distribution information to `qmk doctor` --- lib/python/qmk/cli/doctor/main.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/python/qmk/cli/doctor/main.py b/lib/python/qmk/cli/doctor/main.py index 391353ebbfb..8445c38c15b 100755 --- a/lib/python/qmk/cli/doctor/main.py +++ b/lib/python/qmk/cli/doctor/main.py @@ -16,6 +16,34 @@ from qmk.commands import in_virtualenv from qmk.userspace import qmk_userspace_paths, qmk_userspace_validate, UserspaceValidationError +def distrib_tests(): + def _parse_distrib_info_file(file): + """Parse the QMK distribution info file + """ + try: + vars = {} + with open(file, 'r') as f: + for line in f: + if '=' in line: + key, value = line.split('=', 1) + vars[key.strip()] = value.strip() + return f'{vars.get("TOOLCHAIN_HOST", "unknown")}:{vars.get("TOOLCHAIN_TARGET", "unknown")}:{vars.get("COMMIT_HASH", "unknown")}' + except Exception as e: + cli.log.warning('Error reading QMK distribution info file: %s', e) + return f'Unknown toolchain info file: {file}' + + try: + from qmk.cli import QMK_DISTRIB_DIR + if (QMK_DISTRIB_DIR / 'etc').exists(): + cli.log.info('Found QMK tools distribution directory: {fg_cyan}%s', QMK_DISTRIB_DIR) + toolchains = [_parse_distrib_info_file(file) for file in (QMK_DISTRIB_DIR / 'etc').glob('toolchain_release_*')] + cli.log.info('Found QMK toolchains: {fg_cyan}%s', ', '.join(toolchains)) + except ImportError: + cli.log.info('QMK tools distribution not found.') + + return CheckStatus.OK + + def os_tests(): """Determine our OS and run platform specific tests """ @@ -128,6 +156,7 @@ def doctor(cli): cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE) status = os_status = os_tests() + distrib_tests() userspace_tests(None) From 90ed47945f31ae5b8f412e9609f7e250ceecd0d9 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 22 May 2025 20:07:54 +1000 Subject: [PATCH 33/40] Add `udev` rule installation support. --- docs/driver_installation_zadig.md | 2 +- util/env-bootstrap.sh | 34 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/driver_installation_zadig.md b/docs/driver_installation_zadig.md index 78b631e6469..b1d729d9fe8 100644 --- a/docs/driver_installation_zadig.md +++ b/docs/driver_installation_zadig.md @@ -4,7 +4,7 @@ QMK presents itself to the host as a regular HID keyboard device, and as such re There are two notable exceptions: the Caterina bootloader, usually seen on Pro Micros, and the HalfKay bootloader shipped with PJRC Teensys, appear as a serial port and a generic HID device respectively, and so do not require a driver. -We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the QMK CLI instalation script will have already installed the drivers for you. +We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the QMK CLI installation script will have already installed the drivers for you. ## Installation diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index fde1a955f4a..dfacd693f50 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -16,6 +16,7 @@ # SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli) # SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains) # SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils) +# SKIP_UDEV_RULES: Skip installing the udev rules for Linux. (or: --skip-udev-rules) # SKIP_WINDOWS_DRIVERS: Skip installing the Windows drivers for the flashing utilities. (or: --skip-windows-drivers) # # Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`). @@ -54,6 +55,7 @@ --skip-qmk-cli -- Skip installing the QMK CLI --skip-qmk-toolchains -- Skip installing the QMK toolchains --skip-qmk-flashutils -- Skip installing the QMK flashing utilities + --skip-udev-rules -- Skip installing the udev rules for Linux --skip-windows-drivers -- Skip installing the Windows drivers for the flashing utilities __EOT__ } @@ -387,6 +389,35 @@ __EOT__ tar xf "$target_file" -C "$QMK_DISTRIB_DIR/bin" } + install_linux_udev_rules() { + # Download the udev rules to the toolchains location + echo "Downloading QMK udev rules file..." >&2 + local qmk_rules_target_file="$QMK_DISTRIB_DIR/50-qmk.rules" + download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules" "$qmk_rules_target_file" + + # Install the udev rules -- path list is aligned with qmk doctor's linux.py + local udev_rules_paths=( + /usr/lib/udev/rules.d + /usr/local/lib/udev/rules.d + /run/udev/rules.d + /etc/udev/rules.d + ) + for udev_rules_dir in "${udev_rules_paths[@]}"; do + if [ -d "$udev_rules_dir" ]; then + echo "Installing udev rules to $udev_rules_dir/50-qmk.rules ..." >&2 + $(nsudo) mv "$qmk_rules_target_file" "$udev_rules_dir" + $(nsudo) chown 0:0 "$udev_rules_dir/50-qmk.rules" + $(nsudo) chmod 644 "$udev_rules_dir/50-qmk.rules" + break + fi + done + + # Reload udev rules + echo "Reloading udev rules..." >&2 + $(nsudo) udevadm control --reload-rules + $(nsudo) udevadm trigger + } + install_windows_drivers() { # Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer local latest_driver_installer_release=$(download_url https://api.github.com/repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$') @@ -463,6 +494,9 @@ __EOT__ [ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli [ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains [ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools + if [ "$(uname -s 2>/dev/null || true)" = "Linux" ]; then + [ -n "${SKIP_UDEV_RULES:-}" ] || install_linux_udev_rules + fi if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then [ -n "${SKIP_WINDOWS_DRIVERS:-}" ] || install_windows_drivers fi From 5920274d70f7df645c561190c7688b0f25f8eded Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 22 May 2025 20:30:20 +1000 Subject: [PATCH 34/40] Add warning about distro-provided `qmk` packages. --- docs/newbs_getting_started.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/newbs_getting_started.md b/docs/newbs_getting_started.md index 58407ca8fee..1bc658b9b13 100644 --- a/docs/newbs_getting_started.md +++ b/docs/newbs_getting_started.md @@ -87,6 +87,10 @@ curl -fsSL https://install.qmk.fm | sh **Note for WSL users**: By default, the installation process will clone the QMK repository into your WSL home directory, but if you have cloned manually, ensure that it is located inside the WSL instance instead of the Windows filesystem (ie. not in `/mnt`), as accessing it is currently [extremely slow](https://github.com/microsoft/WSL/issues/4197). ::: +::: warning +Any QMK packages provided by your distribution's package manager are almost certainly out of date. It is strongly suggested the installation script above is used instead. +::: + ==== FreeBSD #### Installation From cb46402a5ff685d0c2d47f3d66beb81e86a6ff78 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 24 May 2025 09:34:15 +1000 Subject: [PATCH 35/40] Apply suggestions from code review Co-authored-by: Joel Challis --- docs/faq_build.md | 2 +- lib/python/qmk/cli/doctor/check.py | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/faq_build.md b/docs/faq_build.md index 91831296b03..05cb3251c8e 100644 --- a/docs/faq_build.md +++ b/docs/faq_build.md @@ -13,7 +13,7 @@ An example of using `sudo`, when your controller is ATMega32u4: or just: - $ qmk flash -kb -km + $ sudo make ::flash Note that running `make` with `sudo` is generally ***not*** a good idea, and you should use one of the former methods, if possible. diff --git a/lib/python/qmk/cli/doctor/check.py b/lib/python/qmk/cli/doctor/check.py index 6ce6b1d8efd..80c4eec2f64 100644 --- a/lib/python/qmk/cli/doctor/check.py +++ b/lib/python/qmk/cli/doctor/check.py @@ -17,18 +17,10 @@ class CheckStatus(Enum): ESSENTIAL_BINARIES = { - 'make': { - 'version_arg': '--version' - }, - 'git': { - 'version_arg': '--version' - }, - 'dos2unix': { - 'version_arg': '--version' - }, - 'diff': { - 'version_arg': '--version' - }, + 'make': {}, + 'git': {}, + 'dos2unix': {}, + 'diff': {}, 'dfu-programmer': {}, 'avrdude': {}, 'dfu-util': {}, From 558cce683f5ab01274f3d8ca4a3982d0c924e506 Mon Sep 17 00:00:00 2001 From: Pascal Getreuer Date: Fri, 4 Apr 2025 20:33:13 -0700 Subject: [PATCH 36/40] Elaborate error message when a binary is missing. --- lib/python/qmk/cli/doctor/check.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/python/qmk/cli/doctor/check.py b/lib/python/qmk/cli/doctor/check.py index 80c4eec2f64..6ca476d704d 100644 --- a/lib/python/qmk/cli/doctor/check.py +++ b/lib/python/qmk/cli/doctor/check.py @@ -176,16 +176,24 @@ def check_binaries(): """Iterates through ESSENTIAL_BINARIES and tests them. """ ok = CheckStatus.OK + missing_from_path = [] for binary in sorted(ESSENTIAL_BINARIES): try: - if not is_executable(binary): + if not is_in_path(binary): + ok = CheckStatus.ERROR + missing_from_path.append(binary) + elif not is_executable(binary): ok = CheckStatus.ERROR except TimeoutExpired: cli.log.debug('Timeout checking %s', binary) if ok != CheckStatus.ERROR: ok = CheckStatus.WARNING + if missing_from_path: + location_noun = 'its location' if len(missing_from_path) == 1 else 'their locations' + cli.log.error('{fg_red}' + ', '.join(missing_from_path) + f' may need to be installed, or {location_noun} added to your path.') + return ok @@ -228,15 +236,18 @@ def check_submodules(): return CheckStatus.OK -def is_executable(command): - """Returns True if command exists and can be executed. +def is_in_path(command): + """Returns True if command is found in the path. """ - # Make sure the command is in the path. - res = shutil.which(command) - if res is None: + if shutil.which(command) is None: cli.log.error("{fg_red}Can't find %s in your path.", command) return False + return True + +def is_executable(command): + """Returns True if command can be executed. + """ # Make sure the command can be executed version_arg = ESSENTIAL_BINARIES[command].get('version_arg', '--version') check = cli.run([command, version_arg], combined_output=True, stdin=DEVNULL, timeout=5) From 4d7764f7f6edd3589b80fe62ce4bb386e74ec609 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 4 Jun 2025 23:00:31 +1000 Subject: [PATCH 37/40] bash-isms not welcome here. --- util/env-bootstrap.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index dfacd693f50..8e3bf6719cd 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -396,13 +396,13 @@ __EOT__ download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules" "$qmk_rules_target_file" # Install the udev rules -- path list is aligned with qmk doctor's linux.py - local udev_rules_paths=( + local udev_rules_paths=" /usr/lib/udev/rules.d /usr/local/lib/udev/rules.d /run/udev/rules.d /etc/udev/rules.d - ) - for udev_rules_dir in "${udev_rules_paths[@]}"; do + " + for udev_rules_dir in $udev_rules_paths; do if [ -d "$udev_rules_dir" ]; then echo "Installing udev rules to $udev_rules_dir/50-qmk.rules ..." >&2 $(nsudo) mv "$qmk_rules_target_file" "$udev_rules_dir" From 70ce81479050b71f5be2a7b01becbb4e16da1f55 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 4 Jun 2025 23:04:27 +1000 Subject: [PATCH 38/40] Allow for missing `udevadm` (i.e. GHA) --- util/env-bootstrap.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index 8e3bf6719cd..d71a6f1d0a2 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -413,9 +413,13 @@ __EOT__ done # Reload udev rules - echo "Reloading udev rules..." >&2 - $(nsudo) udevadm control --reload-rules - $(nsudo) udevadm trigger + if command -v udevadm >/dev/null 2>&1; then + echo "Reloading udev rules..." >&2 + $(nsudo) udevadm control --reload-rules + $(nsudo) udevadm trigger + else + echo "udevadm not found, skipping udev rules reload." >&2 + fi } install_windows_drivers() { From 44ec55032ebe724bc2106eca29ef39eee7888d57 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 4 Jun 2025 23:45:24 +1000 Subject: [PATCH 39/40] `sh`. --- util/env-bootstrap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index d71a6f1d0a2..fda6a84a1a3 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # Copyright 2025 Nick Brassel (@tzarc) # SPDX-License-Identifier: GPL-2.0-or-later From 2c8b8ef713980b2bd14f7fa8fe426d1ac29560c8 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 19 Jun 2025 10:04:02 +1000 Subject: [PATCH 40/40] Add CachyOS as apparently some installs don't include `$ID_LIKE`. --- util/env-bootstrap.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/env-bootstrap.sh b/util/env-bootstrap.sh index fda6a84a1a3..c4c669ef12f 100755 --- a/util/env-bootstrap.sh +++ b/util/env-bootstrap.sh @@ -189,7 +189,7 @@ __EOT__ windows) echo "base-devel: zstd:x toolchain:x clang:x hidapi:x dos2unix: git: unzip:" ;; linux) case $(grep ID /etc/os-release) in - *arch* | *manjaro*) echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; + *arch* | *manjaro* | *cachyos*) echo "zstd base-devel clang diffutils unzip wget zip hidapi dos2unix git" ;; *debian* | *ubuntu*) echo "zstd build-essential clang-format diffutils unzip wget zip libhidapi-hidraw0 dos2unix git" ;; *fedora*) echo "zstd clang diffutils gcc git unzip wget zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git" ;; *gentoo*) echo "app-arch/zstd app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang sys-apps/hwloc dev-libs/hidapi app-text/dos2unix dev-vcs/git" ;; @@ -240,7 +240,7 @@ __EOT__ ;; linux) case $(grep ID /etc/os-release) in - *arch* | *manjaro*) + *arch* | *manjaro* | *cachyos*) echo "It will also install the following system packages using 'pacman':" >&2 print_package_manager_deps_and_delay $(nsudo) pacman --needed --noconfirm -S $(get_package_manager_deps)