Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/omarchy-install-browser
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ zen)
omarchy-pkg-aur-add zen-browser-bin || exit 1

browser_policy_setup_firefox_distribution /opt/zen-browser/distribution
browser_policy_setup_zen_policies
setup_firefox_wayland
omarchy-lifecycle-dispatch post-install zen
announce_browser_installed "Zen"
Expand Down
6 changes: 6 additions & 0 deletions bin/omarchy-remove-browser
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# omarchy:examples=omarchy remove browser firefox | omarchy remove browser brave
# omarchy:requires-sudo=true

source "$OMARCHY_PATH/install/helpers/browser-policy.sh"

set_fallback_default_browser() {
local current_browser
current_browser=$(omarchy-cmd-default-browser)
Expand Down Expand Up @@ -62,6 +64,10 @@ zen)
echo "Removing Zen..."
set_fallback_default_browser zen.desktop
omarchy-pkg-drop zen-browser-bin

if omarchy-pkg-missing zen-browser-bin; then
browser_policy_remove_zen_policies
fi
;;
*)
echo "Usage: omarchy-remove-browser <chrome|brave|brave-origin|edge|firefox|zen>"
Expand Down
49 changes: 49 additions & 0 deletions install/helpers/browser-policy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ BROWSER_POLICY_FIREFOX_DIRS=(
/opt/zen-browser/distribution
)

BROWSER_POLICY_ZEN_DIR=/etc/zen/policies
BROWSER_POLICY_ZEN_PACKAGED=/opt/zen-browser-bin/distribution/policies.json
BROWSER_POLICY_ZEN_MARKER="Written by Omarchy from zen-browser-bin's policies; removing Zen through Omarchy deletes it."

BROWSER_POLICY_DEFAULT_COLOR="#1c2027"

browser_policy_purge_dir() {
Expand Down Expand Up @@ -193,3 +197,48 @@ browser_policy_setup_firefox_distribution() {
browser_policy_purge_dir "$distribution_dir"
browser_policy_install_firefox_policies "$distribution_dir" "$policies"
}

# zen-browser-bin owns its distribution/policies.json, so each package update
# drops an edit made there. Zen reads /etc/zen/policies/policies.json in its
# place whenever that exists, so start it from the package's policies and keep
# Omarchy's setting where pacman never writes. Zen reads only "policies", so the
# marker beside it tells removal which file Omarchy wrote.
browser_policy_setup_zen_policies() {
local policy_dir=${1:-$BROWSER_POLICY_ZEN_DIR}
local packaged=${2:-$BROWSER_POLICY_ZEN_PACKAGED}
local tmp

if [[ ! -e $policy_dir/policies.json && ! -L $policy_dir/policies.json ]]; then
browser_policy_firefox_policy_file_ok "$packaged" || return 0

tmp=$(mktemp) || return 1
if jq --arg marker "$BROWSER_POLICY_ZEN_MARKER" '{__COMMENT__: $marker} + .' "$packaged" >"$tmp" 2>/dev/null; then
browser_policy_setup_parent "${policy_dir%/*}"
browser_policy_setup_parent "$policy_dir"
as_root install -m 644 -o root -g root -T "$tmp" "$policy_dir/policies.json"
fi
rm -f "$tmp"
fi

browser_policy_firefox_skip_default_check "$policy_dir"
}

# Delete the policies.json Omarchy wrote for Zen, then its directories if
# nothing else is in them. An administrator's own file stays, as Firefox's
# policies.json does when Firefox is removed.
browser_policy_remove_zen_policies() {
local policy_dir=${1:-$BROWSER_POLICY_ZEN_DIR}
local file=$policy_dir/policies.json
local dir

if browser_policy_firefox_policy_file_ok "$file" &&
[[ $(jq -r '.__COMMENT__ // empty' "$file" 2>/dev/null) == "$BROWSER_POLICY_ZEN_MARKER" ]]; then
as_root rm -f -- "$file"
fi

for dir in "$policy_dir" "${policy_dir%/*}"; do
if [[ -d $dir && ! -L $dir ]] && [[ -z $(ls -A -- "$dir") ]]; then
as_root rmdir -- "$dir"
fi
done
}
5 changes: 5 additions & 0 deletions migrations/1792363203.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
echo "Stop Zen asking to be the default behind Omarchy's link routing"

source "$OMARCHY_PATH/install/helpers/browser-policy.sh"

browser_policy_setup_zen_policies
2 changes: 1 addition & 1 deletion test/shell.d/platform-app-hooks-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ chmod -R go-w "$lifecycle"
stub_bin=$tmp/bin
omarchy_path=$tmp/omarchy
mkdir -p "$stub_bin" "$omarchy_path/install/helpers" "$omarchy_path/config"
printf 'browser_policy_setup_dir() { :; }\nbrowser_policy_setup_firefox_distribution() { :; }\nas_root() { "$@"; }\n' \
printf 'browser_policy_setup_dir() { :; }\nbrowser_policy_setup_firefox_distribution() { :; }\nbrowser_policy_setup_zen_policies() { :; }\nas_root() { "$@"; }\n' \
>"$omarchy_path/install/helpers/browser-policy.sh"
cp "$ROOT/config/chromium-flags.conf" "$omarchy_path/config/"
for command in omarchy-pkg-add omarchy-pkg-aur-add omarchy-pkg-drop omarchy-install-gaming-gpu-lib32; do
Expand Down
160 changes: 160 additions & 0 deletions test/shell.d/zen-default-browser-check-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

require_command jq

migration="$ROOT/migrations/1792363203.sh"
test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT

export TEST_TMP=$test_tmp
export FAKE_ROOT=$test_tmp/root
zen_dist=$FAKE_ROOT/opt/zen-browser-bin/distribution
zen_etc=$FAKE_ROOT/etc/zen/policies
mock_bin=$test_tmp/bin
mkdir -p "$mock_bin" "$test_tmp/home" "$test_tmp/tmp"

# sudo runs a command only when every path it names is in the scratch tree,
# without the root ownership flags. Anything else is recorded, never run.
cat >"$mock_bin/sudo" <<'SH'
#!/bin/bash
args=()
skip=0
for arg in "$@"; do
if (( skip )); then
skip=0
continue
fi
case $arg in
-o | -g) skip=1; continue ;;
/*)
if [[ $arg != "$TEST_TMP"/* ]]; then
printf '%s\n' "$*" >>"$TEST_TMP/outside"
exit 0
fi
;;
esac
args+=("$arg")
done
"${args[@]}"
SH

# Scratch files stand in for root-owned ones.
cat >"$mock_bin/stat" <<'SH'
#!/bin/bash
if [[ $1 == "-c" && $2 == "%U" && $3 == "$TEST_TMP"/* ]]; then
echo root
else
exec /usr/bin/stat "$@"
fi
SH

printf '#!/bin/bash\nexit 0\n' >"$mock_bin/omarchy-pkg-aur-add"
# Removal: the package goes away, and Zen is not the default browser.
printf '#!/bin/bash\nrm -rf "$FAKE_ROOT/opt/zen-browser-bin"\n' >"$mock_bin/omarchy-pkg-drop"
printf '#!/bin/bash\n[[ ! -e $FAKE_ROOT/opt/zen-browser-bin ]]\n' >"$mock_bin/omarchy-pkg-missing"
printf '#!/bin/bash\necho chromium.desktop\n' >"$mock_bin/omarchy-cmd-default-browser"
printf '#!/bin/bash\nexit 0\n' >"$mock_bin/omarchy-lifecycle-dispatch"
chmod +x "$mock_bin"/*

# The real helper with Zen's two paths moved under FAKE_ROOT. as_root skips sudo
# as root, so route it through the stub or the installer writes the host's /opt.
omarchy_path=$test_tmp/omarchy
mkdir -p "$omarchy_path/install/helpers"
ln -s "$ROOT/default" "$omarchy_path/default"
cat >"$omarchy_path/install/helpers/browser-policy.sh" <<'SH'
source "$ROOT/install/helpers/browser-policy.sh"
BROWSER_POLICY_ZEN_DIR=$FAKE_ROOT$BROWSER_POLICY_ZEN_DIR
BROWSER_POLICY_ZEN_PACKAGED=$FAKE_ROOT$BROWSER_POLICY_ZEN_PACKAGED
as_root() { sudo "$@"; }
SH
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Resolved

package_policies='{"policies": {"DisableAppUpdate": true, "DefaultSerialGuardSetting": 3}}'
expected='{"DisableAppUpdate":true,"DefaultSerialGuardSetting":3,"DontCheckDefaultBrowser":true}'

# What zen-browser-bin writes on every install and upgrade.
install_package() {
install -d -m 0755 "$zen_dist"
printf '%s\n' "$package_policies" >"$zen_dist/policies.json"
chmod 0644 "$zen_dist/policies.json"
}

# The file Zen reads: /etc/zen/policies/policies.json when it exists, else
# the one in its distribution directory.
zen_policies() {
local file=$zen_etc/policies.json
[[ -e $file ]] || file=$zen_dist/policies.json
jq -c '.policies' "$file"
}

run_installer() {
HOME="$test_tmp/home" OMARCHY_PATH="$omarchy_path" PATH="$mock_bin:$PATH" TMPDIR="$test_tmp/tmp" \
"$ROOT/bin/omarchy-install-browser" zen >/dev/null || fail "the Zen installer finishes"
}

run_remover() {
HOME="$test_tmp/home" OMARCHY_PATH="$omarchy_path" PATH="$mock_bin:$PATH" TMPDIR="$test_tmp/tmp" \
"$ROOT/bin/omarchy-remove-browser" zen >/dev/null || fail "the Zen remover finishes"
}

run_migration() {
HOME="$test_tmp/home" OMARCHY_PATH="$omarchy_path" PATH="$mock_bin:$PATH" TMPDIR="$test_tmp/tmp" \
bash -euo pipefail "$migration" >/dev/null || fail "the Zen migration runs"
}

defaults=$(bash -c 'source "$1"; printf "%s %s" "$BROWSER_POLICY_ZEN_DIR" "$BROWSER_POLICY_ZEN_PACKAGED"' _ "$ROOT/install/helpers/browser-policy.sh")
[[ $defaults == "/etc/zen/policies /opt/zen-browser-bin/distribution/policies.json" ]] ||
fail "the helper keeps Zen's setting in /etc/zen/policies, seeded from zen-browser-bin's policies" "$defaults"
pass "the helper keeps Zen's setting in /etc/zen/policies, seeded from zen-browser-bin's policies"

[[ -f $migration ]] || fail "a migration adds Zen's default browser setting"
rm -rf "$FAKE_ROOT"
run_migration
[[ ! -e $FAKE_ROOT/etc/zen ]] || fail "the migration writes nothing without zen-browser-bin"
pass "the migration writes nothing without zen-browser-bin"

install_package
run_migration
run_migration
[[ $(zen_policies) == "$expected" ]] ||
fail "the migration adds DontCheckDefaultBrowser to Zen's policies and keeps the package's" "$(zen_policies)"
install_package
[[ $(zen_policies) == "$expected" ]] ||
fail "Zen keeps DontCheckDefaultBrowser after zen-browser-bin rewrites its policies.json" "$(zen_policies)"
mode=$(stat -c '%a' "$zen_etc/policies.json")
[[ $mode == "644" ]] || fail "Zen's system policies are 0644" "mode=$mode"
pass "the migration stops Zen's default browser prompt, and it survives a package update"

rm -rf "$FAKE_ROOT"
install_package
run_installer
[[ $(zen_policies) == "$expected" ]] ||
fail "the installer adds DontCheckDefaultBrowser to Zen's policies and keeps the package's" "$(zen_policies)"
install_package
[[ $(zen_policies) == "$expected" ]] ||
fail "Zen keeps DontCheckDefaultBrowser after zen-browser-bin rewrites its policies.json" "$(zen_policies)"
pass "the installer stops Zen's default browser prompt, and it survives a package update"

run_remover
[[ ! -e $FAKE_ROOT/etc/zen ]] || fail "removing Zen deletes the /etc/zen Omarchy wrote" "$(find "$FAKE_ROOT/etc/zen")"
pass "removing Zen deletes the /etc/zen Omarchy wrote"

rm -rf "$FAKE_ROOT"
install_package
install -d -m 0755 "$zen_etc"
printf '{"policies": {"DisableTelemetry": true}}\n' >"$zen_etc/policies.json"
run_installer
[[ $(zen_policies) == '{"DisableTelemetry":true,"DontCheckDefaultBrowser":true}' ]] ||
fail "an administrator's /etc/zen policies are kept and get the setting beside them" "$(zen_policies)"
run_remover
[[ $(jq -c '.policies' "$zen_etc/policies.json") == '{"DisableTelemetry":true,"DontCheckDefaultBrowser":true}' ]] ||
fail "removing Zen keeps an administrator's /etc/zen policies"
pass "an administrator's /etc/zen policies are kept, through install and removal"

if [[ -e $test_tmp/outside ]] && grep -E '/etc|zen-browser-bin' "$test_tmp/outside" >/dev/null; then
fail "Zen's policies are written only inside the scratch tree" "$(cat "$test_tmp/outside")"
fi
pass "Zen's policies are written only inside the scratch tree"