#!/bin/bash # BleachBit xzm Module Builder for Porteus # Version: 2026-09-25 # # Copyright 2023-2030, Blaze, Dankov, Russia # All rights reserved. # # Redistribution and use of this script, with or without modification, is # permitted provided that the following conditions are met: # # 1. Redistributions of this script must retain the above copyright # notice, this list of conditions and the following disclaimer. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. export LC_ALL=C export XZ_OPT="-T0" PRGNAM=${PRGNAM:-bleachbit} BUILD=${BUILD:-1} OUTPUT=${OUTPUT:-/tmp} TMPDIR=/tmp/portch SRC="$TMPDIR/src" PKG="$TMPDIR/package-$PRGNAM" PKGINFO="$PKG/var/lib/pkgtools/packages" ARCH=$(uname -m) BOLD=$'\033[1m' CYAN=$'\033[96m' GREEN=$'\033[92m' YELLOW=$'\033[93m' RED=$'\033[91m' RESET=$'\033[0m' cleanup() { rm -rf "$TMPDIR" 2>/dev/null } trap cleanup EXIT INT TERM HUP [ "$(id -u)" -ne 0 ] && { printf "\n${RED}Error: You need to be root to run this script.${RESET}\n"; exit 1; } get_latest_tag() { local tag="" if command -v curl >/dev/null 2>&1; then tag=$(curl -fsSL --max-time 20 \ "https://api.github.com/repos/bleachbit/bleachbit/releases/latest" 2>/dev/null | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1) fi if [ -z "$tag" ] && command -v wget >/dev/null 2>&1; then tag=$(wget --spider -S "https://github.com/bleachbit/bleachbit/releases/latest" 2>&1 | grep -i "Location:" | sed -n 's#.*/tag/##p' | tail -n1 | tr -d '\r') fi if [ -z "$tag" ] && command -v lynx >/dev/null 2>&1; then tag=$(lynx -head -dump "https://github.com/bleachbit/bleachbit/releases/latest" 2>/dev/null | grep -i "Location:" | sed -n 's#.*/tag/##p' | head -n1 | tr -d '\r') fi printf '%s' "$tag" } printf "\nFetching latest release info...\n" TAG=$(get_latest_tag) [[ -z "$TAG" ]] && { printf "${RED}Failed to determine version.${RESET}\n"; exit 1; } VERSION=${TAG#v} SOURCE="https://github.com/bleachbit/bleachbit/archive/refs/tags/${TAG}.tar.gz" if [[ -f "/usr/bin/bleachbit" ]]; then MYVER=$(basename /var/lib/pkgtools/packages/bleachbit-* 2>/dev/null | cut -d'-' -f2 | sort -V | tail -n1) printf "[${GREEN}OK${RESET}] Installed version: ${YELLOW}%s${RESET}\n" "${MYVER:-Unknown}" if [[ "${MYVER:-}" == "$VERSION" ]]; then printf "You already have the latest ${GREEN}%s${RESET} version of %s.\n" "$MYVER" "$PRGNAM" exit 0 fi else printf "[${CYAN}INFO${RESET}] BleachBit is not currently installed.\n" fi printf "Current: ${YELLOW}${MYVER:-None}${RESET} | Available: ${GREEN}%s${RESET}\n" "$VERSION" read -r -p "Build ${PRGNAM} ${GREEN}${VERSION}${RESET} xzm module? [y/N]: " -n 1 -s && echo [[ ! $REPLY =~ ^[Yy]$ ]] && { printf "Build cancelled.\n"; exit 0; } rm -rf "$TMPDIR" mkdir -p "$SRC" "$PKG/usr/bin" "$PKG/usr/share/applications" "$OUTPUT" cd "$TMPDIR" || exit 1 printf "\nDownloading %s%s%s${RESET}\n" "$CYAN" "$BOLD" "bleachbit-${TAG}.tar.gz" if command -v wget >/dev/null 2>&1; then wget -q "$SOURCE" -O "$TMPDIR/$PRGNAM.tar.gz" || { printf "${RED}Download failed.${RESET}\n"; exit 1; } elif command -v curl >/dev/null 2>&1; then curl -fsL --max-time 120 "$SOURCE" -o "$TMPDIR/$PRGNAM.tar.gz" || { printf "${RED}Download failed.${RESET}\n"; exit 1; } else printf "${RED}Neither wget nor curl is available.${RESET}\n" exit 1 fi tar -xf "$TMPDIR/$PRGNAM.tar.gz" -C "$SRC" --strip-components=1 || { printf "${RED}Failed to extract.${RESET}\n"; exit 1; } rm -f "$TMPDIR/$PRGNAM.tar.gz" [[ -f "$SRC/Makefile" && -f "$SRC/bleachbit.py" ]] || { printf "${RED}Source validation failed.${RESET}\n"; exit 1; } cd "$SRC" || exit 1 printf "Building translations...\n" make -s -C po local >/dev/null 2>&1 || { printf "${RED}Failed to build translations.${RESET}\n"; exit 1; } printf "Installing to package directory...\n" make -s install DESTDIR="$PKG" prefix=/usr >/dev/null 2>&1 || { printf "${RED}Installation failed.${RESET}\n"; exit 1; } [[ -f "$PKG/usr/bin/bleachbit" ]] || { printf "${RED}Build failed: bleachbit binary not found.${RESET}\n"; exit 1; } setup_locale() { local ldir="$PKG/usr/share/locale" [[ ! -d "$ldir" ]] && return 0 local locales=($(find "$ldir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)) local count=${#locales[@]} [[ "$count" -eq 0 ]] && return 0 printf "\n${CYAN}BleachBit locale setup${RESET}\n" printf "Available locales (%s):\n" "$count" local COLS=5 local ROWS=$(( (count + COLS - 1) / COLS )) for ((i=0; i Locale set to: ${GREEN}%s${RESET} (English also kept)\n" "$target" find "$ldir" -mindepth 1 -maxdepth 1 -type d -not -name "en*" -not -name "$target" -exec rm -rf {} + } setup_locale printf "\n${CYAN}Cleaning up unnecessary files...${RESET}\n" BB_SHARE="$PKG/usr/share/bleachbit" CLEANERS="$BB_SHARE/cleaners" printf " -> Removing Windows-specific cleaners...\n" rm -f "$CLEANERS/internet_explorer.xml" \ "$CLEANERS/windows_explorer.xml" \ "$CLEANERS/microsoft_office.xml" \ "$CLEANERS/smartftp.xml" \ "$CLEANERS/tortoisesvn.xml" \ "$CLEANERS/wordpad.xml" rm -f "$CLEANERS"/win*.xml printf " -> Removing Windows-specific Python modules and bytecode...\n" rm -f "$BB_SHARE/Winapp.py" "$BB_SHARE"/Windows*.py find "$BB_SHARE/__pycache__" -maxdepth 1 -type f \ \( -name 'Winapp.*' -o -name 'Windows.*' -o -name 'WindowsWipe.*' \) \ -delete 2>/dev/null printf " -> Removing documentation and metadata...\n" rm -rf "$PKG/usr/share/doc" \ "$PKG/usr/share/man" \ "$PKG/usr/share/metainfo" \ "$PKG/usr/share/icons" \ "$PKG/usr/include" printf " -> Removing text files and tests...\n" rm -f "$BB_SHARE"/README* \ "$BB_SHARE"/COPYING* \ "$BB_SHARE"/NEWS* \ "$BB_SHARE"/AUTHORS* \ "$BB_SHARE"/THANKS* \ "$BB_SHARE"/ChangeLog* \ "$BB_SHARE"/CHANGES* \ "$BB_SHARE"/LICENSE* rm -rf "$BB_SHARE/tests" find "$PKG/usr/lib" -type d -path '*bleachbit*/tests' -exec rm -rf {} + 2>/dev/null printf " -> Removing optimized Python bytecode...\n" find "$PKG" -type f -name "*.opt-*.pyc" -delete 2>/dev/null printf " -> Removing empty directories...\n" find "$PKG" -mindepth 1 -type d -empty -delete 2>/dev/null printf " -> Fixing desktop icon path...\n" desktop_file="$PKG/usr/share/applications/org.bleachbit.BleachBit.desktop" if [[ -f "$desktop_file" ]]; then sed -i \ -e 's|^Icon=bleachbit$|Icon=/usr/share/pixmaps/bleachbit.png|' \ -e 's|^Icon=org.bleachbit.BleachBit$|Icon=/usr/share/pixmaps/bleachbit.png|' \ "$desktop_file" fi printf " -> Generating metadata...\n" cd "$PKG" || exit 1 mkdir -p "$PKGINFO" { printf "PACKAGE NAME: %s-%s-%s\n" "$PRGNAM" "$VERSION" "$ARCH" printf "PACKAGE DESCRIPTION:\n" printf "bleachbit: BleachBit (system cleaner and privacy tool)\n" printf "bleachbit: Frees disk space and maintains privacy.\n" printf "bleachbit: Homepage: https://www.bleachbit.org/\n" printf "FILE LIST:\n" find . -type f ! -path './var/*' -printf '%P\n' | sort } > "$PKGINFO/${PRGNAM}-${VERSION}-${ARCH}" printf "\n${CYAN}Building XZM module...${RESET}\n" FINAL_MODULE="$OUTPUT/${PRGNAM}-${VERSION}-${ARCH}-${BUILD}.xzm" rm -f "$FINAL_MODULE" if ! dir2xzm "$PKG" "$FINAL_MODULE" >/dev/null 2>&1; then printf "\n${RED}${BOLD}[FAIL] Failed to build the module.${RESET}\n\n" exit 1 fi if [ -f "$FINAL_MODULE" ]; then MODULE_SIZE=$(du -h "$FINAL_MODULE" | cut -f1) printf "\n${BOLD}[OK] Module successfully created!${RESET}\n" printf "${BOLD}Location: ${GREEN}%s${RESET}\n" "$FINAL_MODULE" printf "${BOLD}Size: ${CYAN}%s${RESET}\n" "$MODULE_SIZE" printf "${BOLD}Action: Copy it to your /porteus/modules folder to survive a reboot.${RESET}\n\n" else printf "\n${RED}${BOLD}[FAIL] Failed to build the module.${RESET}\n\n" exit 1 fi