testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/lib/leechblock_browsers.sh
Krzysztof kuhy Rudnicki 4f2b0ccdf7 Dissolve linux_configuration/scripts/ and single_use/
Two levels of pure taxonomy carrying no information, and a 2-directory
cap leaves no room for them. Categories now sit directly under
linux_configuration/: features, fixes, utils, misc, fresh-install,
installers, diagnostics, gaming, boot_recovery, periodic_background, lib,
meta, tests. A script's own lib/ and tests/ are exempt from the cap, so
those stay put.

Depth violations 248 -> 139; roughly 40 of the remainder are inside
digital_wellbeing and hosts, deferred until the guard-lib repoint.

The moves are the easy half. What breaks is every path that counted
levels, and three classes of it had to be found by resolving paths rather
than reading diffs:

  - 52 relative `source` lines for lib/common.sh and lib/android.sh. The
    correct prefix differs per file: 40 were ../../ and 12 were ../../../,
    so a blanket replace would have fixed one set and broken the other.
  - REPO_ROOT in 7 fixes/lib/tests harnesses and setup_syncyomi_guard.sh,
    each walking up four levels from a directory now two levels shallower.
    This caused a real regression -- the fixes suite went red with
    "log_warn: command not found" -- caught by running it and comparing
    against the pre-move baseline worktree.
  - conftest.py's sys.path entries, which took the pytest gate down with
    14 collection errors, and the `# shellcheck source=` directives, which
    took shellcheck down with SC1091.

16 symlinks in $HOME broke, including the entire i3blocks bar. All
repointed, and cpu_monitor/memory/disk/time/volume were run to confirm
they emit real output rather than merely resolving.

Verified: 1798 tests pass at 100% coverage, 8 of 9 lib/tests suites pass
(the ninth needs the coverage jail, identically at baseline), pre-commit
is clean, and the two untracked secret configs are still ignored after
their gitignore paths changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 18:53:30 +02:00

185 lines
6.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Browser detection and launcher wiring for install_leechblock.sh.
# Sourced by the installer; inherits its strict mode and info/warn helpers.
#
# detect_browsers, replace_browser_in_place and wire_up_browsers live in ONE
# file on purpose: detect_browsers writes BROWSERS/FIREFOXES, which the other
# two read. Splitting them would make those globals write-only across a file
# boundary — a genuine SC2034 the hook cannot see past (it runs shellcheck
# without -x), and suppressions are forbidden.
detect_browsers() {
# Detect browsers
declare -gA BROWSERS
BROWSERS=(
[chromium]="Chromium"
["google-chrome-stable"]="Google Chrome"
["google-chrome"]="Google Chrome"
["brave-browser"]="Brave"
["vivaldi-stable"]="Vivaldi"
[vivaldi]="Vivaldi"
[opera]="Opera"
["thorium-browser"]="Thorium"
)
declare -gA FIREFOXES
FIREFOXES=(
[firefox]="Firefox"
["firefox-developer-edition"]="Firefox Developer Edition"
[librewolf]="LibreWolf"
)
found_any=0
# Create a user desktop entry
user_apps_dir="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
mkdir -p "$user_apps_dir"
}
# Inject --load-extension into a browser launcher so every launch includes LeechBlock.
# Handles two cases:
# 1) The binary is a shell script with an "exec" line — patch it in-place.
# 2) The binary is a compiled ELF — wrap it with a shell script.
# Follows symlinks only one level to avoid breaking shared wrapper scripts
# (e.g. browser-preexec-wrapper used by multiple browser symlinks).
# Requires sudo for system paths.
replace_browser_in_place() {
local bin="$1"
shift
local pretty="$1"
shift
local bin_path
bin_path=$(command -v "$bin" || true)
[[ -z $bin_path ]] && return
# Resolve symlinks to find the actual file to patch.
# Use readlink -f to get the canonical path.
local real_bin
real_bin=$(readlink -f "$bin_path")
local load_ext_flag="--load-extension=\"$EXT_PATH\""
# If already patched, skip (idempotent)
if grep -q -- "$load_ext_flag" "$real_bin" 2>/dev/null; then
info "$pretty ($bin) already has LeechBlock --load-extension — skipping"
found_any=1
return
fi
# Case 1: Shell script with an exec line — patch the exec line directly.
# This preserves the original script's logic (e.g. basename routing,
# hosts enforcement) and avoids breaking shared wrapper scripts.
if file "$real_bin" 2>/dev/null | grep -qi 'text\|script'; then
if grep -qE '^exec ' "$real_bin"; then
info "Patching exec line in $real_bin to add LeechBlock…"
# Kill running instances so the patched script takes effect
pkill -f "$real_bin" 2>/dev/null || true
sleep 1
# Back up before patching (only once)
local orig_backup="${real_bin}.orig"
if [[ ! -f $orig_backup ]]; then
info "Backing up $real_bin → $orig_backup"
sudo cp -a "$real_bin" "$orig_backup"
fi
# Insert --load-extension right after "exec <command>" on the exec line.
# Matches: exec "$real_bin" "$@" or exec /path/to/bin $FLAGS "$@"
sudo sed -i "s|^exec \(.*\) \"\\\$@\"|exec \1 $load_ext_flag \"\\\$@\"|" "$real_bin"
info "✓ $pretty exec line patched with LeechBlock"
found_any=1
return
fi
fi
# Case 2: Binary or script without a recognisable exec line — wrap it.
local orig_backup="${real_bin}.orig"
# Kill running instances
info "Killing running $pretty instances…"
pkill -f "$real_bin" 2>/dev/null || true
pkill -f "$(basename "$real_bin")" 2>/dev/null || true
sleep 1
# Back up original
if [[ ! -f $orig_backup ]]; then
info "Backing up $real_bin → $orig_backup"
sudo cp -a "$real_bin" "$orig_backup"
else
info "Backup already exists: $orig_backup"
fi
# Write replacement wrapper
info "Replacing $real_bin with LeechBlock wrapper…"
sudo tee "$real_bin" >/dev/null <<WRAP
#!/usr/bin/env bash
# __LEECHBLOCK_WRAPPER__ — auto-generated by install_leechblock.sh
# Original backed up at: $orig_backup
exec "$orig_backup" --load-extension="$EXT_PATH" "\$@"
WRAP
sudo chmod +x "$real_bin"
info "✓ $pretty now always launches with LeechBlock"
found_any=1
}
wire_up_browsers() {
# The Firefox guidance and the closing summary interpolate the install
# layout. Those are set by resolve_version, which the entry script always
# runs first; defaulting them keeps this function callable on its own
# without aborting under `set -u` on an unbound VERSION_DIR.
local version_dir="${VERSION_DIR:-<not yet resolved>}"
local version="${VERSION:-<unknown>}"
local tag="${TAG:-<unknown>}"
info "Detecting installed browsers…"
for bin in "${!BROWSERS[@]}"; do
if command -v "$bin" >/dev/null 2>&1; then
replace_browser_in_place "$bin" "${BROWSERS[$bin]}"
fi
done
# Global on purpose: written and read only within this file, and the entry
# script forwards it to install_firefox_policy as an argument.
ff_found=0
for bin in "${!FIREFOXES[@]}"; do
if command -v "$bin" >/dev/null 2>&1; then
ff_found=1
fi
done
echo
if [[ $found_any -eq 1 ]]; then
info "Chromium-based integration complete. Launch the browser via its '(LeechBlock)' launcher."
warn "Chromium will mark it as a developer extension; this is expected for unpacked installs."
fi
if [[ $ff_found -eq 1 ]]; then
echo
warn "Detected Firefox-based browser(s). Permanent install from GitHub source isn't possible on stable builds due to required signing."
cat <<FF
Options:
1) Install from Mozilla Add-ons (recommended):
https://addons.mozilla.org/firefox/addon/leechblock-ng/
2) For testing with Developer Edition or Nightly, you can set xpinstall.signatures.required=false
and install a built XPI. We'll still keep the downloaded source at:
$version_dir
To load temporarily for testing (session-only), open 'about:debugging#/runtime/this-firefox' and "Load Temporary Add-on…" then select $version_dir/manifest.json.
Tip: Re-run this script with --install-firefox to auto-install from AMO via enterprise policy (requires sudo).
FF
fi
if [[ $found_any -eq 0 && $ff_found -eq 0 ]]; then
warn "No supported browsers detected. We placed the extension at: $version_dir"
echo "Supported (auto-wired): ${!BROWSERS[*]}. Detected Firefox variants will show guidance only."
fi
echo
info "Done. Version: $version (tag $tag) installed under $version_dir"
}