sync current state
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DEFAULT_USERS=(kari owen romy)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SYSTEM_EXT_DIR="/usr/share/gnome-shell/extensions"
|
||||
DEFAULT_EXTENSIONS_FILE="${SCRIPT_DIR}/extensions.txt"
|
||||
|
||||
DCONF_PROFILE_FILE="/etc/dconf/profile/user"
|
||||
DCONF_DEFAULTS_FILE="/etc/dconf/db/local.d/00-gnome-extensions"
|
||||
DCONF_LOCKS_FILE="/etc/dconf/db/local.d/locks/gnome-extensions"
|
||||
|
||||
DRY_RUN=0
|
||||
LOCK_DEFAULT=0
|
||||
REFRESH_EXISTING=0
|
||||
USERS=("${DEFAULT_USERS[@]}")
|
||||
EXTENSIONS_FILE="${DEFAULT_EXTENSIONS_FILE}"
|
||||
|
||||
EXTENSION_UUIDS=()
|
||||
EXTENSION_SOURCES=()
|
||||
EXTENSIONS_BASE_DIR=""
|
||||
|
||||
print_usage() {
|
||||
cat <<USAGE
|
||||
Usage: sudo bash scripts/apply-extension-defaults.sh [options]
|
||||
|
||||
Options:
|
||||
--extensions-file PATH Extensions list file (default: scripts/extensions.txt)
|
||||
--users user1,user2 Override target users (default: kari,owen,romy)
|
||||
--lock-default Lock dconf defaults
|
||||
--refresh-existing Reinstall even if extension dir already exists
|
||||
--dry-run Print actions without writing changes
|
||||
-h, --help Show help
|
||||
|
||||
extensions.txt format:
|
||||
<uuid> <source>
|
||||
|
||||
Supported source values:
|
||||
- local zip path (relative or absolute)
|
||||
- zip URL (https://...zip)
|
||||
- git URL (https://...git)
|
||||
- git URL with subdir (https://...git#path/to/extension)
|
||||
USAGE
|
||||
}
|
||||
|
||||
run_cmd() {
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
printf '[dry-run] '
|
||||
printf '%q ' "$@"
|
||||
echo
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
run_cmd_may_fail() {
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
printf '[dry-run] '
|
||||
printf '%q ' "$@"
|
||||
echo
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
write_file() {
|
||||
local path="$1"
|
||||
local content="$2"
|
||||
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo "[dry-run] write ${path}"
|
||||
echo "$content"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$path")"
|
||||
printf '%s\n' "$content" > "$path"
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--extensions-file)
|
||||
EXTENSIONS_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--users)
|
||||
IFS=',' read -r -a USERS <<< "$2"
|
||||
shift 2
|
||||
;;
|
||||
--lock-default)
|
||||
LOCK_DEFAULT=1
|
||||
shift
|
||||
;;
|
||||
--refresh-existing)
|
||||
REFRESH_EXISTING=1
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
ensure_root() {
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
|
||||
echo "Run as root (sudo) unless using --dry-run." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_uuid() {
|
||||
local metadata_file="$1"
|
||||
sed -n 's/.*"uuid"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$metadata_file" | head -n1
|
||||
}
|
||||
|
||||
resolve_source_path() {
|
||||
local source="$1"
|
||||
if [[ "$source" = /* ]]; then
|
||||
printf '%s' "$source"
|
||||
else
|
||||
printf '%s/%s' "$EXTENSIONS_BASE_DIR" "$source"
|
||||
fi
|
||||
}
|
||||
|
||||
is_zip_source() {
|
||||
local source="$1"
|
||||
[[ "$source" == *.zip ]] || [[ "$source" == *.zip\?* ]]
|
||||
}
|
||||
|
||||
is_url_source() {
|
||||
local source="$1"
|
||||
[[ "$source" =~ ^https?:// ]]
|
||||
}
|
||||
|
||||
normalize_extension_permissions() {
|
||||
local target_dir="$1"
|
||||
run_cmd chown -R root:root "$target_dir"
|
||||
run_cmd find "$target_dir" -type d -exec chmod 755 {} +
|
||||
run_cmd find "$target_dir" -type f -exec chmod 644 {} +
|
||||
}
|
||||
|
||||
copy_dir_into_system_extensions() {
|
||||
local source_dir="$1"
|
||||
local uuid="$2"
|
||||
local target_dir="${SYSTEM_EXT_DIR}/${uuid}"
|
||||
|
||||
if [[ ! -f "$source_dir/metadata.json" ]]; then
|
||||
echo "Invalid extension source for $uuid: metadata.json missing in $source_dir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$(extract_uuid "$source_dir/metadata.json")" != "$uuid" ]]; then
|
||||
echo "UUID mismatch for $uuid in $source_dir/metadata.json" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
run_cmd mkdir -p "$target_dir"
|
||||
run_cmd cp -a "$source_dir/." "$target_dir/"
|
||||
normalize_extension_permissions "$target_dir"
|
||||
echo "System-wide extension ensured: $uuid"
|
||||
}
|
||||
|
||||
resolve_extension_dir_from_tree() {
|
||||
local tree_root="$1"
|
||||
local uuid="$2"
|
||||
local subdir="$3"
|
||||
local candidate
|
||||
local metadata
|
||||
|
||||
if [[ -n "$subdir" ]]; then
|
||||
candidate="$tree_root/$subdir"
|
||||
if [[ -f "$candidate/metadata.json" ]] && [[ "$(extract_uuid "$candidate/metadata.json")" == "$uuid" ]]; then
|
||||
printf '%s' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
echo "Subdir '$subdir' does not contain extension $uuid" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -f "$tree_root/metadata.json" ]] && [[ "$(extract_uuid "$tree_root/metadata.json")" == "$uuid" ]]; then
|
||||
printf '%s' "$tree_root"
|
||||
return 0
|
||||
fi
|
||||
|
||||
while IFS= read -r metadata; do
|
||||
if [[ "$(extract_uuid "$metadata")" == "$uuid" ]]; then
|
||||
printf '%s' "$(dirname "$metadata")"
|
||||
return 0
|
||||
fi
|
||||
done < <(find "$tree_root" -maxdepth 6 -type f -name metadata.json)
|
||||
|
||||
echo "Could not locate extension $uuid in source tree" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
install_from_zip_source() {
|
||||
local uuid="$1"
|
||||
local source="$2"
|
||||
local work_dir
|
||||
local zip_path
|
||||
local extract_dir
|
||||
local ext_dir
|
||||
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
if is_url_source "$source"; then
|
||||
run_cmd curl -fsSL "$source" -o /tmp/extension.zip
|
||||
run_cmd unzip -q /tmp/extension.zip -d /tmp/extension-unzip
|
||||
else
|
||||
zip_path="$(resolve_source_path "$source")"
|
||||
run_cmd unzip -q "$zip_path" -d /tmp/extension-unzip
|
||||
fi
|
||||
echo "[dry-run] resolve extension directory for $uuid from zip"
|
||||
run_cmd mkdir -p "${SYSTEM_EXT_DIR}/${uuid}"
|
||||
run_cmd cp -a /tmp/extension-unzip/. "${SYSTEM_EXT_DIR}/${uuid}/"
|
||||
normalize_extension_permissions "${SYSTEM_EXT_DIR}/${uuid}"
|
||||
echo "System-wide extension ensured: $uuid"
|
||||
return 0
|
||||
fi
|
||||
|
||||
work_dir="$(mktemp -d)"
|
||||
extract_dir="$work_dir/unzip"
|
||||
mkdir -p "$extract_dir"
|
||||
|
||||
if is_url_source "$source"; then
|
||||
zip_path="$work_dir/source.zip"
|
||||
curl -fsSL "$source" -o "$zip_path"
|
||||
else
|
||||
zip_path="$(resolve_source_path "$source")"
|
||||
if [[ ! -f "$zip_path" ]]; then
|
||||
echo "Missing zip source for $uuid: $zip_path" >&2
|
||||
rm -rf "$work_dir"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
unzip -q "$zip_path" -d "$extract_dir"
|
||||
ext_dir="$(resolve_extension_dir_from_tree "$extract_dir" "$uuid" "")"
|
||||
copy_dir_into_system_extensions "$ext_dir" "$uuid"
|
||||
rm -rf "$work_dir"
|
||||
}
|
||||
|
||||
install_from_git_source() {
|
||||
local uuid="$1"
|
||||
local source="$2"
|
||||
local repo_url
|
||||
local subdir=""
|
||||
local work_dir
|
||||
local repo_dir
|
||||
local ext_dir
|
||||
|
||||
repo_url="${source%%#*}"
|
||||
if [[ "$source" == *"#"* ]]; then
|
||||
subdir="${source#*#}"
|
||||
fi
|
||||
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
run_cmd git clone --depth 1 "$repo_url" /tmp/extension-repo
|
||||
echo "[dry-run] resolve extension directory for $uuid (subdir: ${subdir:-auto})"
|
||||
run_cmd mkdir -p "${SYSTEM_EXT_DIR}/${uuid}"
|
||||
run_cmd cp -a /tmp/extension-repo/. "${SYSTEM_EXT_DIR}/${uuid}/"
|
||||
normalize_extension_permissions "${SYSTEM_EXT_DIR}/${uuid}"
|
||||
echo "System-wide extension ensured: $uuid"
|
||||
return 0
|
||||
fi
|
||||
|
||||
work_dir="$(mktemp -d)"
|
||||
repo_dir="$work_dir/repo"
|
||||
git clone --depth 1 "$repo_url" "$repo_dir" >/dev/null 2>&1
|
||||
ext_dir="$(resolve_extension_dir_from_tree "$repo_dir" "$uuid" "$subdir")"
|
||||
copy_dir_into_system_extensions "$ext_dir" "$uuid"
|
||||
rm -rf "$work_dir"
|
||||
}
|
||||
|
||||
install_extension() {
|
||||
local uuid="$1"
|
||||
local source="$2"
|
||||
local target_dir="${SYSTEM_EXT_DIR}/${uuid}"
|
||||
|
||||
if [[ -f "$target_dir/metadata.json" && $REFRESH_EXISTING -eq 0 ]]; then
|
||||
normalize_extension_permissions "$target_dir"
|
||||
echo "System-wide extension already present: $uuid"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if is_zip_source "$source"; then
|
||||
install_from_zip_source "$uuid" "$source"
|
||||
return
|
||||
fi
|
||||
|
||||
if is_url_source "$source"; then
|
||||
install_from_git_source "$uuid" "$source"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Unsupported source for $uuid: $source" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
load_extensions_file() {
|
||||
local file="$1"
|
||||
local line
|
||||
local lineno=0
|
||||
local uuid
|
||||
local source
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "Missing extensions file: $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXTENSIONS_BASE_DIR="$(cd "$(dirname "$file")" && pwd)"
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
lineno=$((lineno + 1))
|
||||
line="${line%%$'\r'}"
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "${line:0:1}" == "#" ]] && continue
|
||||
|
||||
uuid="${line%%[[:space:]]*}"
|
||||
source="${line#"$uuid"}"
|
||||
source="$(echo "$source" | sed 's/^[[:space:]]*//')"
|
||||
|
||||
if [[ -z "$uuid" || -z "$source" || "$uuid" == "$line" ]]; then
|
||||
echo "Invalid line ${lineno} in ${file}: expected '<uuid> <source>'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXTENSION_UUIDS+=("$uuid")
|
||||
EXTENSION_SOURCES+=("$source")
|
||||
done < "$file"
|
||||
|
||||
if [[ ${#EXTENSION_UUIDS[@]} -eq 0 ]]; then
|
||||
echo "No extensions loaded from ${file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Loaded ${#EXTENSION_UUIDS[@]} extension entries from ${file}"
|
||||
}
|
||||
|
||||
install_system_wide_extensions() {
|
||||
local i
|
||||
local failures=0
|
||||
|
||||
for i in "${!EXTENSION_UUIDS[@]}"; do
|
||||
if ! install_extension "${EXTENSION_UUIDS[$i]}" "${EXTENSION_SOURCES[$i]}"; then
|
||||
echo "Failed to install ${EXTENSION_UUIDS[$i]} from ${EXTENSION_SOURCES[$i]}" >&2
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $failures -gt 0 ]]; then
|
||||
echo "System-wide install failed for ${failures} extension(s)." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
json_array_from_uuids() {
|
||||
local out="["
|
||||
local first=1
|
||||
local uuid
|
||||
|
||||
for uuid in "${EXTENSION_UUIDS[@]}"; do
|
||||
if [[ $first -eq 0 ]]; then
|
||||
out+=", "
|
||||
fi
|
||||
out+="'${uuid}'"
|
||||
first=0
|
||||
done
|
||||
|
||||
out+="]"
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
apply_dconf_defaults() {
|
||||
local extensions_variant
|
||||
extensions_variant="$(json_array_from_uuids)"
|
||||
|
||||
if [[ -f "$DCONF_PROFILE_FILE" ]]; then
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo "[dry-run] ensure ${DCONF_PROFILE_FILE} contains system-db:local"
|
||||
elif ! rg -q '^system-db:local$' "$DCONF_PROFILE_FILE"; then
|
||||
printf '%s\n' 'system-db:local' >> "$DCONF_PROFILE_FILE"
|
||||
fi
|
||||
else
|
||||
write_file "$DCONF_PROFILE_FILE" $'user-db:user\nsystem-db:local'
|
||||
fi
|
||||
|
||||
write_file "$DCONF_DEFAULTS_FILE" "[org/gnome/shell]
|
||||
enabled-extensions=${extensions_variant}
|
||||
disable-user-extensions=false"
|
||||
|
||||
if [[ $LOCK_DEFAULT -eq 1 ]]; then
|
||||
write_file "$DCONF_LOCKS_FILE" $'/org/gnome/shell/enabled-extensions\n/org/gnome/shell/disable-user-extensions'
|
||||
fi
|
||||
|
||||
run_cmd dconf update
|
||||
}
|
||||
|
||||
apply_user_settings() {
|
||||
local extensions_variant
|
||||
extensions_variant="$(json_array_from_uuids)"
|
||||
local failures=0
|
||||
local user
|
||||
local uuid
|
||||
|
||||
for user in "${USERS[@]}"; do
|
||||
if ! id "$user" >/dev/null 2>&1; then
|
||||
echo "Skipping unknown user: $user"
|
||||
continue
|
||||
fi
|
||||
|
||||
if run_cmd_may_fail runuser -u "$user" -- dbus-run-session -- gsettings set org.gnome.shell enabled-extensions "$extensions_variant" \
|
||||
&& run_cmd_may_fail runuser -u "$user" -- dbus-run-session -- gsettings set org.gnome.shell disable-user-extensions false; then
|
||||
echo "Applied extension list for user: $user"
|
||||
else
|
||||
echo "Primary gsettings path failed for user: $user, trying dconf fallback"
|
||||
if run_cmd_may_fail runuser -u "$user" -- dbus-run-session -- dconf write /org/gnome/shell/enabled-extensions "$extensions_variant" \
|
||||
&& run_cmd_may_fail runuser -u "$user" -- dbus-run-session -- dconf write /org/gnome/shell/disable-user-extensions false; then
|
||||
echo "Applied extension list for user via dconf fallback: $user"
|
||||
else
|
||||
echo "Failed to apply extension list for user: $user" >&2
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
for uuid in "${EXTENSION_UUIDS[@]}"; do
|
||||
run_cmd_may_fail runuser -u "$user" -- dbus-run-session -- gnome-extensions enable "$uuid" >/dev/null 2>&1 || true
|
||||
done
|
||||
done
|
||||
|
||||
if [[ $failures -gt 0 ]]; then
|
||||
echo "One or more user updates failed (${failures})." >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
parse_args "$@"
|
||||
ensure_root
|
||||
load_extensions_file "$EXTENSIONS_FILE"
|
||||
install_system_wide_extensions
|
||||
apply_dconf_defaults
|
||||
apply_user_settings
|
||||
|
||||
echo "System-wide extension install complete, defaults configured for new users, settings applied to selected existing users."
|
||||
echo "Users should log out and back in once to ensure GNOME Shell loads the new defaults cleanly."
|
||||
@@ -0,0 +1,10 @@
|
||||
# uuid source
|
||||
# source supports local zip paths, zip URLs, and git URLs (+ optional #subdir)
|
||||
|
||||
user-switcher@felixfoertsch.de ../gnome-user-switcher-extension/dist/user-switcher@felixfoertsch.de.shell-extension.zip
|
||||
dash-to-panel@jderose9.github.com https://github.com/home-sweet-gnome/dash-to-panel.git
|
||||
just-perfection-desktop@just-perfection https://gitlab.gnome.org/jrahmatzadeh/just-perfection.git
|
||||
logomenu@aryan_k https://github.com/Aryan20/Logomenu.git
|
||||
drive-menu@gnome-shell-extensions.gcampax.github.com https://gitlab.gnome.org/GNOME/gnome-shell-extensions.git#extensions/drive-menu
|
||||
appindicatorsupport@rgcjonas.gmail.com https://github.com/ubuntu/gnome-shell-extension-appindicator.git
|
||||
caffeine@patapon.info https://github.com/eonpatapon/gnome-shell-extension-caffeine.git
|
||||
Reference in New Issue
Block a user