scripts/install-deps.sh
Rama master · réplica de lectura del repositorio original.
#!/bin/sh
# The shebang is sh, but the script re-executes itself with Bash below, so
# check it as Bash rather than reporting every bashism.
# shellcheck shell=bash
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2008-2026 Andrew Ziem.
#
# Install BleachBit's FreeBSD, Linux, and macOS dependencies for running
# from source and optionally for development, testing, and packaging.
#
# Usage:
# ./scripts/install-deps.sh # runtime deps only
# ./scripts/install-deps.sh --dev # runtime + build/test/packaging deps
# ./scripts/install-deps.sh --venv # runtime deps + create a Python venv
# ./scripts/install-deps.sh --dev --venv
# ./scripts/install-deps.sh --help
#
# --venv creates a virtual environment (default ./.venv, override with
# VENV_DIR=/path) and installs pure-Python dependencies into it via pip.
# On Linux, native libraries (GTK, GObject introspection, etc.) are still
# installed via the system package manager because PyGObject cannot be
# pip-installed reliably. On macOS, GTK/GObject is ignored and only the
# venv is used; with --dev, lint tools come from Homebrew.
#
# Distro is auto-detected from /etc/os-release; macOS is detected via uname;
# FreeBSD is detected via uname -s.
# Supported families:
# debian (Debian, Ubuntu, Mint, etc.) -> apt
# fedora (Fedora, RHEL, Alma, CentOS, etc.) -> dnf
# opensuse (Tumbleweed, Leap) -> zypper
# arch (Arch, Manjaro, etc.) -> pacman
# freebsd (FreeBSD) -> pkg
# macos (macOS) -> pip in venv + Homebrew (dev)
#
# This Bash script works similarly to:
# - .github/workflows/tests.yaml
# - bleachbit.spec
# - debian/debian.control
# - docker/Dockerfile.*
# - requirements.txt
#
# This Bash script installs optional dependencies.
# If this script was invoked with a POSIX shell, re-execute with Bash.
if [ -z "${BASH_VERSION:-}" ]; then
if [ "$(uname -s)" = "FreeBSD" ]; then
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: FreeBSD dependency installation must be run as root." >&2
exit 1
fi
if ! command -v pkg >/dev/null 2>&1; then
# Install the package manager.
pkg bootstrap -y
fi
bash_path="$(command -v bash || true)"
if [ -z "$bash_path" ]; then
# Install Bash.
pkg install -y bash
bash_path=/usr/local/bin/bash
fi
exec "$bash_path" "$0" "$@"
fi
exec bash "$0" "$@"
fi
set -euo pipefail
MODE="runtime"
VENV=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
VENV_DIR="${VENV_DIR:-$REPO_ROOT/.venv}"
usage() {
cat <<'EOF'
Usage: ./scripts/install-deps.sh [--dev] [--venv] [--help]
(no flag) Install runtime dependencies (enough to run from source).
--dev Also install build, test, lint, and packaging tools.
--venv Create a Python virtualenv (.venv) and install pure-Python
deps into it with pip. On Linux, native libs are still
installed via the system package manager. On macOS, GTK/
GObject is ignored and --venv is used; --dev also installs
lint tools via Homebrew. Override the location
with VENV_DIR=/path/to/venv.
--help Show this help.
Distro is auto-detected from /etc/os-release; macOS and FreeBSD are detected
via uname. Supported: debian, fedora, opensuse, arch, freebsd, macos.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--dev) MODE="dev"; shift ;;
--venv) VENV=1; shift ;;
--help|-h) usage; exit 0 ;;
*) echo "ERROR: unknown argument '$1'" >&2; usage >&2; exit 2 ;;
esac
done
# Detect distribution family from /etc/os-release, or macOS/FreeBSD via uname.
detect_distro() {
local os
os="$(uname -s)"
case "$os" in
Darwin) echo "macos"; return ;;
FreeBSD) echo "freebsd"; return ;;
esac
if [[ ! -r /etc/os-release ]]; then
echo "ERROR: /etc/os-release not found; cannot detect distribution." >&2
echo " This script supports Debian, Fedora, openSUSE, Arch, FreeBSD, and macOS." >&2
exit 1
fi
# shellcheck disable=SC1091
. /etc/os-release
case "${ID:-}:${ID_LIKE:-}" in
debian:*|*:*debian*|ubuntu:*|linuxmint:*|pop:*|elementary:*) echo "debian" ;;
fedora:*|*:*fedora*|rhel:*|centos:*|almalinux:*|rocky:*|ol:*) echo "fedora" ;;
opensuse*:*|suse:*|*:suse*) echo "opensuse" ;;
arch:*|*:arch*|manjaro:*|endeavouros:*|garuda:*|cachyos:*) echo "arch" ;;
*)
echo "ERROR: unsupported distribution (ID='${ID:-}', ID_LIKE='${ID_LIKE:-}')." >&2
echo " Supported: debian, fedora, opensuse, arch, freebsd, macos." >&2
exit 1
;;
esac
}
# --- Debian family (apt) -----------------------------------------------------
install_debian() {
local runtime=(
gir1.2-gtk-3.0
gir1.2-notify-0.7
libgtk-3-0
python3
python3-gi
python3-setuptools
xdg-utils
)
local runtime_pip=(
python3-distro
python3-psutil
python3-requests
)
local dev=(
build-essential
debhelper
desktop-file-utils
devscripts
dh-python
dos2unix
fakeroot
gettext
libxml2-utils
lintian
locales
python3-pip
shellcheck
appstream
)
local dev_pip=(
python3-autopep8
python3-pyflakes
python3-pytest
python3-pytest-xdist
pylint
)
if [[ "$VENV" == 1 ]]; then
runtime+=(python3-venv)
fi
echo "[debian] apt-get update"
sudo apt-get update
echo "[debian] installing runtime deps"
sudo apt-get install -y --no-install-recommends "${runtime[@]}"
if [[ "$VENV" == 0 ]]; then
sudo apt-get install -y --no-install-recommends "${runtime_pip[@]}"
fi
if [[ "$MODE" == "dev" ]]; then
echo "[debian] installing dev/test/packaging deps"
sudo apt-get install -y --no-install-recommends "${dev[@]}"
if [[ "$VENV" == 0 ]]; then
sudo apt-get install -y --no-install-recommends "${dev_pip[@]}"
fi
fi
}
# --- Fedora / RHEL family (dnf) ----------------------------------------------
install_fedora() {
local runtime=(
gobject-introspection
gtk3
libnotify
python3
python3-gobject
python3-setuptools
xdg-utils
)
local runtime_pip=(
python3-distro
python3-psutil
python3-requests
)
local dev=(
desktop-file-utils
dos2unix
fdupes
findutils
gettext
glibc-langpack-en
libxml2
make
patch
python3-pip
ShellCheck
appstream
redhat-rpm-config
rpm-build
rpmdevtools
tar
which
xz
)
local dev_pip=(
python3-autopep8
python3-pyflakes
python3-pytest
python3-pytest-xdist
pylint
)
# skip dnf update: focus on just the installation.
echo "[fedora] installing runtime deps"
sudo dnf install -y --setopt=install_weak_deps=False "${runtime[@]}"
if [[ "$VENV" == 0 ]]; then
sudo dnf install -y --setopt=install_weak_deps=False "${runtime_pip[@]}"
fi
if [[ "$MODE" == "dev" ]]; then
echo "[fedora] installing dev/test/packaging deps"
sudo dnf install -y --setopt=install_weak_deps=False "${dev[@]}"
if [[ "$VENV" == 0 ]]; then
sudo dnf install -y --setopt=install_weak_deps=False "${dev_pip[@]}"
fi
fi
sudo dnf clean all
}
# --- openSUSE family (zypper) ------------------------------------------------
detect_opensuse_python_prefix() {
local candidates=()
local candidate
if command -v python3 >/dev/null 2>&1; then
candidates+=("$(python3 -c 'import sys; print("python%d%d" % sys.version_info[:2])')")
fi
candidates+=(python313 python312 python311 python310 python39 python3)
for candidate in "${candidates[@]}"; do
if zypper --non-interactive info --type package "${candidate}-gobject" >/dev/null 2>&1; then
echo "$candidate"
return
fi
done
echo "ERROR: could not find an openSUSE Python GObject package." >&2
exit 1
}
install_opensuse() {
echo "[opensuse] zypper refresh"
sudo zypper --non-interactive refresh
local py
py="$(detect_opensuse_python_prefix)"
echo "[opensuse] using Python package prefix: $py"
local runtime=(
gobject-introspection
gtk3
libnotify
python3
"${py}-gobject"
"${py}-gobject-Gdk"
"${py}-setuptools"
update-desktop-files
xdg-utils
)
local runtime_pip=(
"${py}-psutil"
"${py}-requests"
)
# distro is a tiny pure-Python module not always packaged on openSUSE.
local runtime_pip_extra=("${py}-distro")
local dev=(
desktop-file-utils
dos2unix
fdupes
gettext-tools
libxml2-tools
make
"${py}-pip"
"${py}-xml"
python-rpm-macros
rpm-build
rpmdevtools
ShellCheck
timezone
)
# sqlite-utils is not packaged on Leap (see bleachbit.spec); install if available.
local dev_extra=("${py}-sqlite-utils")
local dev_pip=(
"${py}-pytest"
"${py}-pytest-xdist"
)
# Lint tools on openSUSE are packaged differently; install via pip
# in dev mode if the system packages are unavailable.
echo "[opensuse] installing runtime deps"
sudo zypper --non-interactive install --no-recommends "${runtime[@]}"
if [[ "$VENV" == 0 ]]; then
sudo zypper --non-interactive install --no-recommends "${runtime_pip[@]}"
# distro module: install if available, else it comes via pip/venv.
sudo zypper --non-interactive install --no-recommends "${runtime_pip_extra[@]}" || true
fi
if [[ "$MODE" == "dev" ]]; then
echo "[opensuse] installing dev/test/packaging deps"
sudo zypper --non-interactive install --no-recommends "${dev[@]}"
sudo zypper --non-interactive install --no-recommends "${dev_extra[@]}" || true
if [[ "$VENV" == 0 ]]; then
sudo zypper --non-interactive install --no-recommends "${dev_pip[@]}" || true
# pylint/pyflakes/autopep8 may not be in the main repos;
# fall back to pip --user for the lint tooling used by `make pretty`/`make lint`.
if ! command -v pylint >/dev/null 2>&1; then
echo "[opensuse] installing lint tools via pip --user"
pip install --user --break-system-packages -r "$REPO_ROOT/requirements-lint.txt" || \
pip install --user -r "$REPO_ROOT/requirements-lint.txt"
fi
fi
fi
sudo zypper clean --all
}
# --- Arch family (pacman) ----------------------------------------------------
install_arch() {
local runtime=(
gtk3
gobject-introspection
libnotify
python
python-gobject
python-setuptools
xdg-utils
)
local runtime_pip=(
python-distro
python-psutil
python-requests
)
local dev=(
appstream
base-devel
desktop-file-utils
dos2unix
gettext
libxml2
python-pip
shellcheck
)
local dev_pip=(
python-autopep8
python-pyflakes
python-pytest
python-pytest-xdist
pylint
)
# no `pacman -Sy` to avoid partial upgrade.
# no `pacman -Syu` to avoid full upgrade that user may not want now.
echo "[arch] installing runtime deps"
sudo pacman -S --needed --noconfirm "${runtime[@]}"
if [[ "$VENV" == 0 ]]; then
sudo pacman -S --needed --noconfirm "${runtime_pip[@]}"
fi
if [[ "$MODE" == "dev" ]]; then
echo "[arch] installing dev/test/lint deps"
sudo pacman -S --needed --noconfirm "${dev[@]}"
if [[ "$VENV" == 0 ]]; then
sudo pacman -S --needed --noconfirm "${dev_pip[@]}"
fi
fi
}
# --- FreeBSD (pkg) -----------------------------------------------------------
# FreeBSD 15.x ships py3.12 as the default.
# GTK/GObject is omitted for now.
install_freebsd() {
local py="${FREEBSD_PY_PREFIX:-py312}"
if [[ $EUID -ne 0 ]]; then
echo "ERROR: FreeBSD dependency installation must be run as root." >&2
return 1
fi
export ASSUME_ALWAYS_YES=yes
if ! command -v pkg >/dev/null 2>&1; then
echo "[freebsd] bootstrapping pkg"
pkg bootstrap -y
fi
echo "[freebsd] pkg update"
pkg update -q
local runtime=(
lsof # for checking for open files
python3
"${py}-distro"
"${py}-psutil"
"${py}-requests"
"${py}-setuptools"
"${py}-sqlite3" # FreeBSD excludes it by default.
)
local dev=(
gmake # GNU Make for Makefiles
libxml2 # xmllint
gettext-tools # translations
)
echo "[freebsd] installing runtime deps"
pkg install -y "${runtime[@]}"
if [[ "$MODE" == "dev" ]]; then
echo "[freebsd] installing dev/test deps"
pkg install -y "${dev[@]}"
fi
}
# --- macOS (pip in a venv; Homebrew for dev tools) ---------------------------
install_macos() {
echo "[macos] using pip in a venv (GTK/GObject is not installed on macOS)"
if ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: python3 is required but not found." >&2
echo " Install the Xcode Command Line Tools or Python from python.org." >&2
exit 1
fi
if ! python3 -m venv -h >/dev/null 2>&1; then
echo "ERROR: python3 venv module is required but not available." >&2
exit 1
fi
if [[ "$MODE" == "dev" ]]; then
install_macos_dev
echo "[macos] dev/test/lint Python deps will be installed via venv"
fi
}
# The shellcheck, appstreamcli (`make lint`), and dos2unix (`make pretty`)
# tools are not installable via pip; get them from Homebrew.
install_macos_dev() {
local brew_cmd
brew_cmd="$(command -v brew || true)"
# Homebrew may be installed but not on PATH in a non-interactive shell.
if [[ -z "$brew_cmd" ]]; then
local candidate
for candidate in /opt/homebrew/bin/brew /usr/local/bin/brew; do
if [[ -x "$candidate" ]]; then
brew_cmd="$candidate"
break
fi
done
fi
if [[ -n "$brew_cmd" ]]; then
echo "[macos] installing dev/lint tools via Homebrew"
"$brew_cmd" install shellcheck appstream dos2unix
else
echo "WARNING: Homebrew not found; skipping shellcheck, appstream, dos2unix." >&2
echo " Install Homebrew (https://brew.sh) or install them manually." >&2
fi
}
setup_venv() {
local req_file="$REPO_ROOT/requirements.txt"
echo "[venv] creating virtual environment in $VENV_DIR"
if [[ -d "$VENV_DIR" ]]; then
echo "[venv] $VENV_DIR already exists; reusing it"
else
# --system-site-packages: on Linux, python3-gi is installed as a system
# package (see install_debian/install_fedora/etc.), and pip cannot
# install PyGObject reliably. Without this flag, pylint would report
# spurious unresolved-import errors for every `import gi`.
python3 -m venv --system-site-packages "$VENV_DIR"
fi
# shellcheck disable=SC1091
. "$VENV_DIR/bin/activate"
pip install --upgrade pip setuptools
echo "[venv] installing runtime Python deps from $req_file"
pip install -r "$req_file"
if [[ "$MODE" == "dev" ]]; then
echo "[venv] installing dev/test/lint Python deps"
pip install -r "$REPO_ROOT/requirements-test.txt" -r "$REPO_ROOT/requirements-lint.txt"
fi
}
main() {
local distro
distro="$(detect_distro)"
if [[ "$distro" == "macos" && "$VENV" == 0 ]]; then
echo "[macos] defaulting to --venv"
VENV=1
fi
echo "Detected distribution family: $distro"
echo "Mode: $MODE"
if [[ "$VENV" == 1 ]]; then
echo "Virtualenv: $VENV_DIR"
fi
case "$distro" in
debian) install_debian ;;
fedora) install_fedora ;;
opensuse) install_opensuse ;;
arch) install_arch ;;
freebsd) install_freebsd ;;
macos) install_macos ;;
*) echo "ERROR: unsupported distro '$distro'" >&2; exit 1 ;;
esac
if [[ "$VENV" == 1 ]]; then
setup_venv
fi
echo
echo "Done. You can now run BleachBit from source:"
if [[ "$distro" == "freebsd" ]]; then
echo " gmake -C po local # build translations"
elif [[ "$distro" != "macos" ]]; then
echo " make -C po local # build translations"
fi
if [[ "$VENV" == 1 ]]; then
echo " . '$VENV_DIR/bin/activate'"
fi
echo " python3 bleachbit.py"
if [[ "$distro" == "macos" || "$distro" == "freebsd" ]]; then
echo " (${distro} uses the command-line interface for now)"
fi
if [[ "$MODE" == "dev" ]]; then
echo
echo "And run the test suite:"
if [[ "$distro" == "freebsd" ]]; then
echo " gmake tests"
else
echo " make tests"
fi
fi
}
main