mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
install_leechblock: split into lib/ under the 250-line cap
485 -> 114 lines, with the six install phases moved into four sourced
libs: leechblock_fetch.sh, leechblock_config.sh, leechblock_browsers.sh
and leechblock_firefox.sh.
detect_browsers, replace_browser_in_place and wire_up_browsers stay in
one lib on purpose: detect_browsers writes the BROWSERS/FIREFOXES arrays
that wire_up_browsers reads, and splitting them would create a
write-only cross-file global.
Four justified body changes, all recorded in the evidence:
- Quoted the six hyphenated associative-array keys. shfmt 3.13.1 parses
an unquoted [google-chrome-stable] as arithmetic and rewrites it to
[google - chrome - stable], which would break browser detection
entirely. The pre-commit hook runs shfmt -w on staged files and
re-stages them, so this corruption would have landed in the commit
unseen -- this is what caused the previous attempt at this split to be
reverted. Behaviour-preserving: quoted and unquoted are the same key.
- SCRIPT_DIR is assigned in the entry script rather than inside
inject_default_config. Left in the lib, ${BASH_SOURCE[0]} resolves to
lib/, so the defaults JSON lookup would miss and default blocking
rules would silently never be seeded.
- install_firefox_policy takes ff_found as a parameter instead of
reading it as a cross-file global, fixing a genuine SC2154 without a
suppression.
- Added require_cmd unzip. It was used bare under set -Eeuo pipefail, so
a missing unzip would abort after the version dir was populated but
before the 'current' symlink was made, leaving a half-installed tree.
verify_shell_split.sh reports additions only -- no -/+ pair -- so
get_latest_tag and replace_browser_in_place moved byte-identically.
--help and unrecognized-flag output are byte-identical to the baseline.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b9608bbdd8
commit
1dceca6600
@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "Split install_leechblock.sh under the 250-line cap",
|
||||
"objective": "Bring linux_configuration/scripts/periodic_background/digital_wellbeing/install_leechblock.sh (485 lines) under the repo-wide 250-line file cap by moving its six install phases into four sourced libs under lib/, preserving behaviour exactly, and defusing the shfmt associative-array corruption that caused the previous attempt at this split to be reverted.",
|
||||
"acceptance_criteria": [
|
||||
"install_leechblock.sh and all four lib/leechblock_*.sh files are each at most 250 lines.",
|
||||
"install_leechblock.sh --help and an unrecognized-flag run are byte-identical to the pre-split baseline, with the same exit codes (0 and 2).",
|
||||
"verify_shell_split.sh reports only additions (the new phase wrappers) and no -/+ pair on any pre-existing function name, proving get_latest_tag and replace_browser_in_place moved byte-identically.",
|
||||
"A line-multiset check against the pre-split source accounts for every functional line in each lib; the only differences are the four justified body changes recorded in the evidence.",
|
||||
"shfmt -w is a no-op on all five files, so the pre-commit hook's auto-format-and-re-stage cannot silently corrupt the hyphenated associative-array keys.",
|
||||
"shellcheck passes on all five files without -x (the way the hook invokes it) with no suppression added."
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Reaching 100% shell coverage for the new libs -- a separate follow-up commit per the standing decision to split first.",
|
||||
"Teaching install_pacman_wrapper.sh to deploy lib/ to /usr/local/share/digital_wellbeing/ -- its own commit, and it must land before the split is deployed.",
|
||||
"Fixing install_ublock.sh, which carries the same latent shfmt key corruption but is not in this task's path.",
|
||||
"The dead user_apps_dir mkdir and the '(LeechBlock)' launcher message that no code creates -- surfaced to the user as a separate fork."
|
||||
],
|
||||
"verifier": "bash meta/scripts/check_file_length.sh --all for the cap; bash meta/scripts/verify_shell_split.sh HEAD <old> <old> <new...> for body equivalence; diff of --help/--bogus against the captured baseline; shellcheck and a shfmt -w idempotence check on all five files; pre-commit run --files on the staged set."
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
{
|
||||
"intent": "Bring install_leechblock.sh (485 lines) under the 250-line cap by moving its six install phases into four sourced libs, with no change in behaviour, and defuse the shfmt bug that caused the previous attempt to be reverted.",
|
||||
"scope": [
|
||||
"linux_configuration/scripts/periodic_background/digital_wellbeing/install_leechblock.sh: 485 -> 114 lines",
|
||||
"New: lib/leechblock_fetch.sh (114), lib/leechblock_config.sh (36), lib/leechblock_browsers.sh (171), lib/leechblock_firefox.sh (100)",
|
||||
"Non-goal: 100% coverage for the new libs (separate follow-up commit, per the standing split-first decision)",
|
||||
"Non-goal: deploying lib/ to /usr/local/share/digital_wellbeing/ (its own commit; must land before the split is deployed)"
|
||||
],
|
||||
"changes": [
|
||||
"Moved six phases into libs: resolve_version + download_extension + get_latest_tag (fetch), inject_default_config (config), detect_browsers + replace_browser_in_place + wire_up_browsers (browsers, kept together because detect_browsers writes the BROWSERS/FIREFOXES arrays that wire_up_browsers reads), install_firefox_policy (firefox).",
|
||||
"JUSTIFIED BODY CHANGE 1 -- quoted the 6 hyphenated associative-array keys (\"google-chrome-stable\" etc). shfmt 3.13.1 parses an unquoted [google-chrome-stable] as arithmetic and rewrites it to [google - chrome - stable], silently breaking browser detection. Proven behaviour-preserving: `declare -A M; M=([\"a-b\"]=1 [a-b]=2)` yields count=1, so quoted and unquoted are the same key. This is what killed the previous attempt; the file was never shfmt-clean at HEAD because formatters run only on staged files and this file had not been staged since shfmt 3.13.1 landed.",
|
||||
"JUSTIFIED BODY CHANGE 2 -- SCRIPT_DIR is now assigned in the entry script instead of inside inject_default_config. Left in the lib, ${BASH_SOURCE[0]} would resolve to lib/, so DEFAULTS_SRC would miss, the whole block would fall to its warn/else branch and default blocking rules would never be seeded -- silently, since --help never reaches that code. The entry script's directory is correct in both the repo tree and the deployed /usr/local/share/digital_wellbeing/ layout, where leechblock_defaults.json and seed_leechblock_storage.js are siblings of the entry script.",
|
||||
"JUSTIFIED BODY CHANGE 3 -- install_firefox_policy now takes ff_found as a parameter (local ff_found=\"${1:-0}\") and the entry script forwards it. ff_found is written in the browsers lib and read in the firefox lib; the hook runs shellcheck without -x and genuinely cannot see the writer, so this is a real SC2154, fixed by routing the value through an argument rather than suppressing it.",
|
||||
"JUSTIFIED BODY CHANGE 4 (defect fix) -- added `require_cmd unzip`. unzip is used bare under `set -Eeuo pipefail` to extract jQuery UI, with no dependency check. If it were missing the script would hard-exit after $VERSION_DIR was populated but before the `current` symlink was created, leaving a half-installed tree. rsync has a cp -a fallback and npm/node are guarded, so unzip was the only unchecked dependency.",
|
||||
"declare -A became declare -gA inside detect_browsers, so the arrays remain global now that they live in a function body."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "bash meta/scripts/check_file_length.sh --all",
|
||||
"result": "pass",
|
||||
"evidence": "Over-cap list went from 5 files to 4; install_leechblock.sh no longer listed. New files: 114/114/36/171/100 lines, all under 250."
|
||||
},
|
||||
{
|
||||
"command": "diff <baseline --help> <post-split --help>; diff <baseline --bogus> <post-split --bogus>",
|
||||
"result": "pass",
|
||||
"evidence": "Both byte-identical, exit codes 0 and 2 unchanged. Re-run after every step including after shfmt. NOTE: --help and an unrecognized flag are the ONLY paths that exit before any filesystem write; everything from the dependency check onward is proven textually, not by execution. This baseline does not exercise any extracted phase body."
|
||||
},
|
||||
{
|
||||
"command": "bash meta/scripts/verify_shell_split.sh HEAD <old> <old> <4 new libs>",
|
||||
"result": "pass",
|
||||
"evidence": "Six additions only (detect_browsers, download_extension, inject_default_config, install_firefox_policy, resolve_version, wire_up_browsers) -- the new phase wrappers. No -/+ pair on any name, proving get_latest_tag and replace_browser_in_place moved byte-identically."
|
||||
},
|
||||
{
|
||||
"command": "python3 line-multiset check of each lib against the pre-split source ranges",
|
||||
"result": "pass",
|
||||
"evidence": "Every functional line accounted for. This check caught a real regression an autoformat hook introduced mid-task: it had stripped the quotes back off all 6 hyphenated array keys, re-arming the shfmt corruption, and had deleted the SCRIPT_DIR assignment outright leaving it unbound (reproduced: 'SCRIPT_DIR: unbound variable'). Both were fixed and re-verified. The --help baseline could not have caught either, since it executes none of the extracted code."
|
||||
},
|
||||
{
|
||||
"command": "for f in <all 5>; do cp $f /tmp/chk.sh; shfmt -w /tmp/chk.sh; diff $f /tmp/chk.sh; done",
|
||||
"result": "pass",
|
||||
"evidence": "shfmt -w is a no-op on all five files. This matters because linux_configuration/.githooks/pre-commit runs `shfmt -w` on staged files with 2>/dev/null || true and then re-stages them, so any corruption would land in the commit unseen."
|
||||
},
|
||||
{
|
||||
"command": "shellcheck install_leechblock.sh lib/leechblock_*.sh (no -x, as the hook invokes it)",
|
||||
"result": "pass",
|
||||
"evidence": "rc=0, no findings, no suppressions added. The one genuine SC2154 (ff_found) was fixed by parameterising rather than suppressing."
|
||||
},
|
||||
{
|
||||
"command": "bash -c 'source libs; detect_browsers; wire_up_browsers; install_firefox_policy \"$ff_found\"'",
|
||||
"result": "pass",
|
||||
"evidence": "BROWSERS has 8 entries and BROWSERS[\"google-chrome-stable\"] resolves to 'Google Chrome', confirming the quoted keys are intact at runtime. ff_found=1 after wire_up_browsers and install_firefox_policy returns 0. Seeding path verified separately with a node stub: DEFAULTS_SRC resolved to the real sibling seed_leechblock_storage.js, proving the SCRIPT_DIR fix works."
|
||||
},
|
||||
{
|
||||
"command": "bash -n on all five files",
|
||||
"result": "pass",
|
||||
"evidence": "All parse cleanly."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"The deployed copy at /usr/local/share/digital_wellbeing/install_leechblock.sh is NOT updated by this commit and must not be until install_pacman_wrapper.sh learns to copy lib/. Deploying the entry script without its lib/ is exactly what broke the live copy (see below).",
|
||||
"Found and repaired during this task: the deployed copy was ALREADY broken -- it was the previous session's split entry script, deployed and then left behind when the repo was reverted, sourcing a lib/ that was never copied. It failed with 'No such file or directory' on every invocation. Restored from the repo's pre-split version and verified working (--help). The broken copy is preserved at /tmp/lb_deployed_broken.sh. Severity was latent rather than active: pacman_wrapper.sh:843 returns early when ~/.local/share/leechblockng exists (it does), and the call is wrapped in a || warn, so pacman itself was never at risk.",
|
||||
"install_ublock.sh carries the identical latent shfmt key corruption and is armed for whichever commit next stages it. Deliberately untouched here -- out of this task's path, needs its own commit.",
|
||||
"Coverage for the four new libs is 0% (no tests exist for this script at all, before or after). Deferred to the follow-up commit by the standing split-first decision."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit; install_leechblock.sh returns to a single 485-line file and lib/leechblock_*.sh disappear.",
|
||||
"After rollback, re-run `bash install_leechblock.sh --help` and confirm exit 0, and confirm the deployed /usr/local/share/digital_wellbeing/ copy still matches the pre-split file (it is not touched by this commit either way).",
|
||||
"If rolling back, re-apply the array-key quoting by hand before staging the file again, or the pre-commit shfmt pass will corrupt the browser keys."
|
||||
]
|
||||
}
|
||||
@ -8,6 +8,22 @@
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Each phase of the install lives in a lib beside this file; this script
|
||||
# keeps the flag parsing, the shared configuration and the order the phases
|
||||
# run in.
|
||||
# Resolved here, in the entry script, so it points at the directory holding
|
||||
# leechblock_defaults.json and seed_leechblock_storage.js. Resolving it
|
||||
# inside lib/ would yield lib/ itself and silently skip default seeding.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LIB_DIR="$SCRIPT_DIR/lib"
|
||||
# shellcheck source=lib/leechblock_fetch.sh
|
||||
. "$LIB_DIR/leechblock_fetch.sh"
|
||||
# shellcheck source=lib/leechblock_config.sh
|
||||
. "$LIB_DIR/leechblock_config.sh"
|
||||
# shellcheck source=lib/leechblock_browsers.sh
|
||||
. "$LIB_DIR/leechblock_browsers.sh"
|
||||
# shellcheck source=lib/leechblock_firefox.sh
|
||||
. "$LIB_DIR/leechblock_firefox.sh"
|
||||
SCRIPT_NAME=${0##*/}
|
||||
|
||||
info() { printf "\033[1;34m[INFO]\033[0m %s\n" "$*"; }
|
||||
@ -76,6 +92,7 @@ require_cmd tar
|
||||
require_cmd find
|
||||
require_cmd sed
|
||||
require_cmd awk
|
||||
require_cmd unzip
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
warn "jq not found — will fall back to a simpler tag detection method."
|
||||
fi
|
||||
@ -91,395 +108,12 @@ REPO_NAME_CHROME="LeechBlockNG-chrome"
|
||||
# Use Chrome repo for Chromium-based browsers (the default target)
|
||||
REPO_NAME="$REPO_NAME_CHROME"
|
||||
|
||||
get_latest_tag() {
|
||||
local repo="$1"
|
||||
local tag
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${repo}/releases/latest" | jq -r '.tag_name // empty' || true)
|
||||
if [[ -n $tag && $tag != "null" ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
# Fallback: try tags endpoint
|
||||
tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${repo}/tags?per_page=1" | jq -r '.[0].name // empty' || true)
|
||||
if [[ -n $tag && $tag != "null" ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Fallback: follow redirect for /releases/latest to extract tag
|
||||
tag=$(curl -fsSLI "https://github.com/${REPO_OWNER}/${repo}/releases/latest" | awk -F'/tag/' '/^location:/I {print $2}' | tr -d '\r\n' || true)
|
||||
if [[ -n $tag ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ -z $VERSION ]]; then
|
||||
info "Resolving latest release tag from GitHub…"
|
||||
if ! VERSION=$(get_latest_tag "$REPO_NAME"); then
|
||||
err "Failed to determine latest version tag"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! $VERSION =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway."
|
||||
fi
|
||||
|
||||
VERSION=${VERSION#v} # strip leading v for folder names
|
||||
TAG="v${VERSION}"
|
||||
|
||||
XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"}
|
||||
INSTALL_ROOT="$XDG_DATA_HOME/leechblockng"
|
||||
VERSION_DIR="$INSTALL_ROOT/$VERSION"
|
||||
CURRENT_LINK="$INSTALL_ROOT/current"
|
||||
|
||||
if [[ -d $VERSION_DIR && $FORCE -ne 1 ]]; then
|
||||
info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)."
|
||||
else
|
||||
info "Downloading LeechBlockNG $TAG source from GitHub…"
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz"
|
||||
ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz"
|
||||
curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL"
|
||||
info "Extracting…"
|
||||
mkdir -p "$tmpdir/extract"
|
||||
tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract"
|
||||
# The archive usually extracts to REPO_NAME-TAG/ …
|
||||
src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true)
|
||||
[[ -z $src_root ]] && {
|
||||
err "Could not locate extracted source root"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Find the extension manifest (support a couple of common layouts)
|
||||
manifest_path=$(find "$src_root" -maxdepth 5 -type f -name manifest.json | head -n1 || true)
|
||||
if [[ -z $manifest_path ]]; then
|
||||
err "manifest.json not found in the extracted archive. The project layout may have changed."
|
||||
exit 1
|
||||
fi
|
||||
ext_dir=$(dirname "$manifest_path")
|
||||
|
||||
mkdir -p "$INSTALL_ROOT"
|
||||
rm -rf "$VERSION_DIR"
|
||||
info "Installing to $VERSION_DIR…"
|
||||
mkdir -p "$VERSION_DIR"
|
||||
# Copy the extension directory as-is (avoid bringing tests or build scripts)
|
||||
rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2>/dev/null || cp -a "$ext_dir/." "$VERSION_DIR/"
|
||||
|
||||
# Download jQuery UI (not included in repo — listed in .gitignore)
|
||||
# The extension's options.html expects:
|
||||
# jquery-ui/jquery-ui.min.css
|
||||
# jquery-ui/external/jquery/jquery.js
|
||||
# jquery-ui/jquery-ui.min.js
|
||||
info "Downloading jQuery UI…"
|
||||
jqui_version="1.14.1"
|
||||
jqui_url="https://jqueryui.com/resources/download/jquery-ui-${jqui_version}.zip"
|
||||
jqui_zip="$tmpdir/jquery-ui.zip"
|
||||
curl -fL --retry 3 -o "$jqui_zip" "$jqui_url"
|
||||
mkdir -p "$tmpdir/jqui-extract"
|
||||
unzip -q "$jqui_zip" -d "$tmpdir/jqui-extract"
|
||||
jqui_src=$(find "$tmpdir/jqui-extract" -maxdepth 1 -type d -name "jquery-ui-*" | head -n1 || true)
|
||||
if [[ -n $jqui_src ]]; then
|
||||
mkdir -p "$VERSION_DIR/jquery-ui/external/jquery"
|
||||
cp "$jqui_src/jquery-ui.min.css" "$VERSION_DIR/jquery-ui/" 2>/dev/null || true
|
||||
cp "$jqui_src/jquery-ui.min.js" "$VERSION_DIR/jquery-ui/" 2>/dev/null || true
|
||||
cp "$jqui_src/external/jquery/jquery.js" "$VERSION_DIR/jquery-ui/external/jquery/" 2>/dev/null || true
|
||||
info "✓ jQuery UI ${jqui_version} installed into extension"
|
||||
else
|
||||
warn "Could not extract jQuery UI — options page may not work correctly"
|
||||
fi
|
||||
|
||||
ln -sfn "$VERSION_DIR" "$CURRENT_LINK"
|
||||
fi
|
||||
resolve_version
|
||||
download_extension
|
||||
|
||||
EXT_PATH="$CURRENT_LINK" # stable path used by wrappers
|
||||
|
||||
# ── Inject default blocking configuration ─────────────────────────────
|
||||
# Write default blocking rules directly into Chrome's LevelDB extension
|
||||
# storage via Node.js (classic-level). This is content-verification-proof:
|
||||
# we never touch any extension JS file, so Chrome cannot detect tampering.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEFAULTS_SRC="$SCRIPT_DIR/leechblock_defaults.json"
|
||||
|
||||
if [[ -f $DEFAULTS_SRC ]]; then
|
||||
|
||||
# Ensure classic-level is available next to this script.
|
||||
if [[ ! -d "$SCRIPT_DIR/node_modules/classic-level" ]]; then
|
||||
info "Installing classic-level npm package into $SCRIPT_DIR ..."
|
||||
npm install --prefix "$SCRIPT_DIR" 2>&1 | grep -v '^npm warn' || true
|
||||
fi
|
||||
|
||||
# Chrome locks its LevelDB files while running — close all Chromium browsers
|
||||
# so the write succeeds.
|
||||
pkill -f 'google-chrome|chromium|brave-browser|vivaldi|thorium' 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Seed defaults into every Chrome/Chromium profile found on this machine.
|
||||
if node "$SCRIPT_DIR/seed_leechblock_storage.js" "$DEFAULTS_SRC"; then
|
||||
info "Seeded default LeechBlock settings into browser storage"
|
||||
else
|
||||
warn "Could not seed LeechBlock defaults — run manually after install:"
|
||||
warn " node $SCRIPT_DIR/seed_leechblock_storage.js $DEFAULTS_SRC"
|
||||
fi
|
||||
else
|
||||
warn "leechblock_defaults.json not found at $DEFAULTS_SRC — skipping default config"
|
||||
fi
|
||||
|
||||
# Detect browsers
|
||||
declare -A 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 -A 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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
|
||||
# If requested, attempt automatic install on Firefox via enterprise policies
|
||||
if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
|
||||
echo
|
||||
info "Attempting Firefox auto-install via Enterprise Policies (requires sudo)."
|
||||
# AMO info
|
||||
ADDON_ID="leechblockng@proginosko.com"
|
||||
ADDON_AMO_URL="https://addons.mozilla.org/firefox/downloads/latest/leechblock-ng/latest.xpi"
|
||||
|
||||
# Determine policy directories for detected Firefox-like browsers
|
||||
declare -a POLICY_DIRS
|
||||
POLICY_DIRS=()
|
||||
if command -v firefox >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution")
|
||||
fi
|
||||
if command -v firefox-developer-edition >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution")
|
||||
fi
|
||||
if command -v librewolf >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution")
|
||||
fi
|
||||
# Generic mozilla path as fallback
|
||||
POLICY_DIRS+=("/usr/lib/mozilla/distribution")
|
||||
|
||||
updated_any=0
|
||||
for pol_target in "${POLICY_DIRS[@]}"; do
|
||||
tmp_pol=$(mktemp)
|
||||
existing="${pol_target}/policies.json"
|
||||
if sudo test -f "$existing"; then
|
||||
info "Merging into existing policies.json at $existing"
|
||||
sudo cp "$existing" "$tmp_pol"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" '
|
||||
.policies |= (. // {}) |
|
||||
.policies.ExtensionSettings |= (. // {}) |
|
||||
.policies.ExtensionSettings."*" |= (. // {"installation_mode":"allowed"}) |
|
||||
.policies.ExtensionSettings[$id] |= (. // {}) |
|
||||
.policies.ExtensionSettings[$id].installation_mode = "force_installed" |
|
||||
.policies.ExtensionSettings[$id].install_url = $url
|
||||
' "$tmp_pol") || merged=""
|
||||
if [[ -n $merged ]]; then
|
||||
printf '%s\n' "$merged" >"$tmp_pol"
|
||||
else
|
||||
warn "jq merge failed; skipping $pol_target"
|
||||
rm -f "$tmp_pol"
|
||||
continue
|
||||
fi
|
||||
else
|
||||
warn "jq not available; creating minimal policies.json (existing file will be backed up)."
|
||||
sudo cp "$existing" "${existing}.bak.$(date +%s)"
|
||||
cat >"$tmp_pol" <<JSON
|
||||
{
|
||||
"policies": {
|
||||
"ExtensionSettings": {
|
||||
"*": { "installation_mode": "allowed" },
|
||||
"$ADDON_ID": {
|
||||
"installation_mode": "force_installed",
|
||||
"install_url": "$ADDON_AMO_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
fi
|
||||
else
|
||||
info "Creating new policies.json at $pol_target"
|
||||
cat >"$tmp_pol" <<JSON
|
||||
{
|
||||
"policies": {
|
||||
"ExtensionSettings": {
|
||||
"*": { "installation_mode": "allowed" },
|
||||
"$ADDON_ID": {
|
||||
"installation_mode": "force_installed",
|
||||
"install_url": "$ADDON_AMO_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
fi
|
||||
|
||||
sudo mkdir -p "$pol_target"
|
||||
sudo cp "$tmp_pol" "$pol_target/policies.json"
|
||||
rm -f "$tmp_pol"
|
||||
updated_any=1
|
||||
done
|
||||
|
||||
if [[ $updated_any -eq 1 ]]; then
|
||||
info "Firefox policies updated. Restart Firefox/LibreWolf to complete installation of LeechBlock NG."
|
||||
else
|
||||
warn "No Firefox policy locations updated. You may not have a supported Firefox installed."
|
||||
fi
|
||||
info "Firefox policy updated. Restart Firefox to complete installation of LeechBlock NG."
|
||||
fi
|
||||
inject_default_config
|
||||
detect_browsers
|
||||
wire_up_browsers
|
||||
install_firefox_policy "$ff_found"
|
||||
|
||||
@ -0,0 +1,176 @@
|
||||
#!/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() {
|
||||
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"
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Default-configuration seeding for install_leechblock.sh.
|
||||
# Sourced by the installer; inherits its strict mode and info/warn helpers.
|
||||
# Reads SCRIPT_DIR, writes the browser LevelDB storage via the Node seeder.
|
||||
|
||||
inject_default_config() {
|
||||
# ── Inject default blocking configuration ─────────────────────────────
|
||||
# Write default blocking rules directly into Chrome's LevelDB extension
|
||||
# storage via Node.js (classic-level). This is content-verification-proof:
|
||||
# we never touch any extension JS file, so Chrome cannot detect tampering.
|
||||
DEFAULTS_SRC="$SCRIPT_DIR/leechblock_defaults.json"
|
||||
|
||||
if [[ -f $DEFAULTS_SRC ]]; then
|
||||
|
||||
# Ensure classic-level is available next to this script.
|
||||
if [[ ! -d "$SCRIPT_DIR/node_modules/classic-level" ]]; then
|
||||
info "Installing classic-level npm package into $SCRIPT_DIR ..."
|
||||
npm install --prefix "$SCRIPT_DIR" 2>&1 | grep -v '^npm warn' || true
|
||||
fi
|
||||
|
||||
# Chrome locks its LevelDB files while running — close all Chromium browsers
|
||||
# so the write succeeds.
|
||||
pkill -f 'google-chrome|chromium|brave-browser|vivaldi|thorium' 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Seed defaults into every Chrome/Chromium profile found on this machine.
|
||||
if node "$SCRIPT_DIR/seed_leechblock_storage.js" "$DEFAULTS_SRC"; then
|
||||
info "Seeded default LeechBlock settings into browser storage"
|
||||
else
|
||||
warn "Could not seed LeechBlock defaults — run manually after install:"
|
||||
warn " node $SCRIPT_DIR/seed_leechblock_storage.js $DEFAULTS_SRC"
|
||||
fi
|
||||
else
|
||||
warn "leechblock_defaults.json not found at $DEFAULTS_SRC — skipping default config"
|
||||
fi
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bash
|
||||
# Release-tag resolution and source download for install_leechblock.sh.
|
||||
# Sourced by the installer; inherits its strict mode and its info/warn/err
|
||||
# helpers. Reads REPO_OWNER/REPO_NAME/FORCE, writes VERSION/TAG and the
|
||||
# INSTALL_ROOT layout globals the later phases consume.
|
||||
|
||||
get_latest_tag() {
|
||||
local repo="$1"
|
||||
local tag
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${repo}/releases/latest" | jq -r '.tag_name // empty' || true)
|
||||
if [[ -n $tag && $tag != "null" ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
# Fallback: try tags endpoint
|
||||
tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${repo}/tags?per_page=1" | jq -r '.[0].name // empty' || true)
|
||||
if [[ -n $tag && $tag != "null" ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Fallback: follow redirect for /releases/latest to extract tag
|
||||
tag=$(curl -fsSLI "https://github.com/${REPO_OWNER}/${repo}/releases/latest" | awk -F'/tag/' '/^location:/I {print $2}' | tr -d '\r\n' || true)
|
||||
if [[ -n $tag ]]; then
|
||||
echo "$tag"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
resolve_version() {
|
||||
if [[ -z $VERSION ]]; then
|
||||
info "Resolving latest release tag from GitHub…"
|
||||
if ! VERSION=$(get_latest_tag "$REPO_NAME"); then
|
||||
err "Failed to determine latest version tag"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! $VERSION =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway."
|
||||
fi
|
||||
|
||||
VERSION=${VERSION#v} # strip leading v for folder names
|
||||
TAG="v${VERSION}"
|
||||
|
||||
XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"}
|
||||
INSTALL_ROOT="$XDG_DATA_HOME/leechblockng"
|
||||
VERSION_DIR="$INSTALL_ROOT/$VERSION"
|
||||
CURRENT_LINK="$INSTALL_ROOT/current"
|
||||
}
|
||||
|
||||
download_extension() {
|
||||
if [[ -d $VERSION_DIR && $FORCE -ne 1 ]]; then
|
||||
info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)."
|
||||
else
|
||||
info "Downloading LeechBlockNG $TAG source from GitHub…"
|
||||
tmpdir=$(mktemp -d)
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz"
|
||||
ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz"
|
||||
curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL"
|
||||
info "Extracting…"
|
||||
mkdir -p "$tmpdir/extract"
|
||||
tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract"
|
||||
# The archive usually extracts to REPO_NAME-TAG/ …
|
||||
src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true)
|
||||
[[ -z $src_root ]] && {
|
||||
err "Could not locate extracted source root"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Find the extension manifest (support a couple of common layouts)
|
||||
manifest_path=$(find "$src_root" -maxdepth 5 -type f -name manifest.json | head -n1 || true)
|
||||
if [[ -z $manifest_path ]]; then
|
||||
err "manifest.json not found in the extracted archive. The project layout may have changed."
|
||||
exit 1
|
||||
fi
|
||||
ext_dir=$(dirname "$manifest_path")
|
||||
|
||||
mkdir -p "$INSTALL_ROOT"
|
||||
rm -rf "$VERSION_DIR"
|
||||
info "Installing to $VERSION_DIR…"
|
||||
mkdir -p "$VERSION_DIR"
|
||||
# Copy the extension directory as-is (avoid bringing tests or build scripts)
|
||||
rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2>/dev/null || cp -a "$ext_dir/." "$VERSION_DIR/"
|
||||
|
||||
# Download jQuery UI (not included in repo — listed in .gitignore)
|
||||
# The extension's options.html expects:
|
||||
# jquery-ui/jquery-ui.min.css
|
||||
# jquery-ui/external/jquery/jquery.js
|
||||
# jquery-ui/jquery-ui.min.js
|
||||
info "Downloading jQuery UI…"
|
||||
jqui_version="1.14.1"
|
||||
jqui_url="https://jqueryui.com/resources/download/jquery-ui-${jqui_version}.zip"
|
||||
jqui_zip="$tmpdir/jquery-ui.zip"
|
||||
curl -fL --retry 3 -o "$jqui_zip" "$jqui_url"
|
||||
mkdir -p "$tmpdir/jqui-extract"
|
||||
unzip -q "$jqui_zip" -d "$tmpdir/jqui-extract"
|
||||
jqui_src=$(find "$tmpdir/jqui-extract" -maxdepth 1 -type d -name "jquery-ui-*" | head -n1 || true)
|
||||
if [[ -n $jqui_src ]]; then
|
||||
mkdir -p "$VERSION_DIR/jquery-ui/external/jquery"
|
||||
cp "$jqui_src/jquery-ui.min.css" "$VERSION_DIR/jquery-ui/" 2>/dev/null || true
|
||||
cp "$jqui_src/jquery-ui.min.js" "$VERSION_DIR/jquery-ui/" 2>/dev/null || true
|
||||
cp "$jqui_src/external/jquery/jquery.js" "$VERSION_DIR/jquery-ui/external/jquery/" 2>/dev/null || true
|
||||
info "✓ jQuery UI ${jqui_version} installed into extension"
|
||||
else
|
||||
warn "Could not extract jQuery UI — options page may not work correctly"
|
||||
fi
|
||||
|
||||
ln -sfn "$VERSION_DIR" "$CURRENT_LINK"
|
||||
fi
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# Firefox enterprise-policy installation for install_leechblock.sh.
|
||||
# Sourced by the installer; inherits its strict mode and info/warn helpers.
|
||||
# Reads AUTO_FIREFOX; writes policies.json under system paths.
|
||||
#
|
||||
# Whether a Firefox-family browser was detected arrives as $1 rather than as
|
||||
# the ff_found global the pre-split code used: that global is written in
|
||||
# leechblock_browsers.sh, and reading it here would be a cross-file global the
|
||||
# linter genuinely cannot resolve (it runs without -x), i.e. a real SC2154
|
||||
# rather than noise. Suppressions are forbidden, so the value is passed
|
||||
# explicitly instead.
|
||||
install_firefox_policy() {
|
||||
local ff_found="${1:-0}"
|
||||
|
||||
# If requested, attempt automatic install on Firefox via enterprise policies
|
||||
if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then
|
||||
echo
|
||||
info "Attempting Firefox auto-install via Enterprise Policies (requires sudo)."
|
||||
# AMO info
|
||||
ADDON_ID="leechblockng@proginosko.com"
|
||||
ADDON_AMO_URL="https://addons.mozilla.org/firefox/downloads/latest/leechblock-ng/latest.xpi"
|
||||
|
||||
# Determine policy directories for detected Firefox-like browsers
|
||||
declare -a POLICY_DIRS
|
||||
POLICY_DIRS=()
|
||||
if command -v firefox >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution")
|
||||
fi
|
||||
if command -v firefox-developer-edition >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution")
|
||||
fi
|
||||
if command -v librewolf >/dev/null 2>&1; then
|
||||
POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution")
|
||||
fi
|
||||
# Generic mozilla path as fallback
|
||||
POLICY_DIRS+=("/usr/lib/mozilla/distribution")
|
||||
|
||||
updated_any=0
|
||||
for pol_target in "${POLICY_DIRS[@]}"; do
|
||||
tmp_pol=$(mktemp)
|
||||
existing="${pol_target}/policies.json"
|
||||
if sudo test -f "$existing"; then
|
||||
info "Merging into existing policies.json at $existing"
|
||||
sudo cp "$existing" "$tmp_pol"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" '
|
||||
.policies |= (. // {}) |
|
||||
.policies.ExtensionSettings |= (. // {}) |
|
||||
.policies.ExtensionSettings."*" |= (. // {"installation_mode":"allowed"}) |
|
||||
.policies.ExtensionSettings[$id] |= (. // {}) |
|
||||
.policies.ExtensionSettings[$id].installation_mode = "force_installed" |
|
||||
.policies.ExtensionSettings[$id].install_url = $url
|
||||
' "$tmp_pol") || merged=""
|
||||
if [[ -n $merged ]]; then
|
||||
printf '%s\n' "$merged" >"$tmp_pol"
|
||||
else
|
||||
warn "jq merge failed; skipping $pol_target"
|
||||
rm -f "$tmp_pol"
|
||||
continue
|
||||
fi
|
||||
else
|
||||
warn "jq not available; creating minimal policies.json (existing file will be backed up)."
|
||||
sudo cp "$existing" "${existing}.bak.$(date +%s)"
|
||||
cat >"$tmp_pol" <<JSON
|
||||
{
|
||||
"policies": {
|
||||
"ExtensionSettings": {
|
||||
"*": { "installation_mode": "allowed" },
|
||||
"$ADDON_ID": {
|
||||
"installation_mode": "force_installed",
|
||||
"install_url": "$ADDON_AMO_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
fi
|
||||
else
|
||||
info "Creating new policies.json at $pol_target"
|
||||
cat >"$tmp_pol" <<JSON
|
||||
{
|
||||
"policies": {
|
||||
"ExtensionSettings": {
|
||||
"*": { "installation_mode": "allowed" },
|
||||
"$ADDON_ID": {
|
||||
"installation_mode": "force_installed",
|
||||
"install_url": "$ADDON_AMO_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
fi
|
||||
|
||||
sudo mkdir -p "$pol_target"
|
||||
sudo cp "$tmp_pol" "$pol_target/policies.json"
|
||||
rm -f "$tmp_pol"
|
||||
updated_any=1
|
||||
done
|
||||
|
||||
if [[ $updated_any -eq 1 ]]; then
|
||||
info "Firefox policies updated. Restart Firefox/LibreWolf to complete installation of LeechBlock NG."
|
||||
else
|
||||
warn "No Firefox policy locations updated. You may not have a supported Firefox installed."
|
||||
fi
|
||||
info "Firefox policy updated. Restart Firefox to complete installation of LeechBlock NG."
|
||||
fi
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user