Update files-exp; Rename 3.files.yaml to 3.files-exp.yaml

This commit is contained in:
clsty
2025-11-01 09:52:34 +08:00
parent e3549e639e
commit 17984c812f
3 changed files with 152 additions and 147 deletions
+130 -120
View File
@@ -2,19 +2,29 @@
# It's not for directly running. # It's not for directly running.
# See https://github.com/end-4/dots-hyprland/issues/2137 # See https://github.com/end-4/dots-hyprland/issues/2137
#
# Stage 1 todos:
# TODO: add --exp-files-path <path> Use <path> instead of the default yaml config
# TODO: add --exp-files-regen Force copy the default config to ${EXP_FILE_PATH} (auto do this when not existed)
# TODO: Implement versioning, i.e. when user-defined yaml config file has version number mismatch with the default one, produce error. If only minor version number is not the same, the error can be ommitted via --exp-file-no-strict . # TODO: Implement versioning, i.e. when user-defined yaml config file has version number mismatch with the default one, produce error. If only minor version number is not the same, the error can be ommitted via --exp-file-no-strict .
# TODO: Implement user-define yaml with merging (override) ability for user who only wants little customization and is satisfied with most of the defaults. # TODO: add --exp-files-no-strict Ignore error when minor version number is not the same
#
# Stage 2 todos:
# TODO: Implement symlink (readable or non-readable) as sync mode # TODO: Implement symlink (readable or non-readable) as sync mode
# TODO: Implement --exp-file-reset-symlink to try to remove all symlink in .config and .local, which point to the local repo # TODO: add --exp-file-reset-symlink Try to remove all symlink in .config and .local, which point to the local repo
# TODO: Update help and doc about `--exp-files` and the yaml config, including the possible values of mode. # TODO: Update help and doc about `--exp-files` and the yaml config, including the possible values of mode.
#
# Stage 3 todos:
# TODO: Implement user-define yaml with merging (override) ability for user who only wants little customization and is satisfied with most of the defaults.
# TODO: Support exclude pattern for each target to skip some files inside it. # TODO: Support exclude pattern for each target to skip some files inside it.
# Configuration file # Configuration file
CONFIG_FILE="sdata/subcmd-install/3.files.yaml" CONFIG_FILE="sdata/subcmd-install/3.files-exp.yaml"
# ============================================================================= # =============================================================================
wizard_update_preferences() { wizard_update_preferences() {
echo -e "${STY_CYAN}=== Dotfiles Customization ===${STY_RESET}" echo -e "${STY_CYAN}=== Dotfiles Customization ===${STY_RESET}"
# Get current preferences # Get current preferences
current_shell=$(yq '.user_preferences.shell // "fish"' "$CONFIG_FILE") current_shell=$(yq '.user_preferences.shell // "fish"' "$CONFIG_FILE")
@@ -34,9 +44,9 @@ wizard_update_preferences() {
read -p "Enter choice [1-2]: " shell_choice read -p "Enter choice [1-2]: " shell_choice
case "$shell_choice" in case "$shell_choice" in
1|"") shell="fish" ;; 1|"") shell="fish" ;;
2) shell="zsh" ;; 2) shell="zsh" ;;
*) echo "Invalid choice, using fish"; shell="fish" ;; *) echo "Invalid choice, using fish"; shell="fish" ;;
esac esac
# Terminal selection # Terminal selection
@@ -47,9 +57,9 @@ wizard_update_preferences() {
read -p "Enter choice [1-2]: " terminal_choice read -p "Enter choice [1-2]: " terminal_choice
case "$terminal_choice" in case "$terminal_choice" in
1|"") terminal="kitty" ;; 1|"") terminal="kitty" ;;
2) terminal="foot" ;; 2) terminal="foot" ;;
*) echo "Invalid choice, using kitty"; terminal="kitty" ;; *) echo "Invalid choice, using kitty"; terminal="kitty" ;;
esac esac
# Keybindings selection # Keybindings selection
@@ -60,9 +70,9 @@ wizard_update_preferences() {
read -p "Enter choice [1-2]: " keybind_choice read -p "Enter choice [1-2]: " keybind_choice
case "$keybind_choice" in case "$keybind_choice" in
1|"") keybindings="default" ;; 1|"") keybindings="default" ;;
2) keybindings="vim" ;; 2) keybindings="vim" ;;
*) echo "Invalid choice, using default"; keybindings="default" ;; *) echo "Invalid choice, using default"; keybindings="default" ;;
esac esac
# Update YAML in-place # Update YAML in-place
@@ -72,17 +82,17 @@ wizard_update_preferences() {
echo echo
echo "Preferences updated!" echo "Preferences updated!"
} }
# Get user preference # Get user preference
get_pref() { get_pref() {
yq -r ".user_preferences.$1" "$CONFIG_FILE" yq -r ".user_preferences.$1" "$CONFIG_FILE"
} }
# Check if pattern should be processed based on user preferences # Check if pattern should be processed based on user preferences
should_process_pattern() { should_process_pattern() {
local pattern="$1" local pattern="$1"
local condition=$(echo "$pattern" | yq '.condition // "true"') local condition=$(echo "$pattern" | yq '.condition // "true"')
# If no condition or condition is "true", always process # If no condition or condition is "true", always process
if [[ "$condition" == "true" ]]; then if [[ "$condition" == "true" ]]; then
@@ -95,46 +105,46 @@ should_process_pattern() {
[[ "$(get_pref "$type")" == "$value" ]] [[ "$(get_pref "$type")" == "$value" ]]
} }
# Compare hashes of files/directories, return true if they are the same, false otherwise # Compare hashes of files/directories, return true if they are the same, false otherwise
files_are_same() { files_are_same() {
local path1="$1" local path1="$1"
local path2="$2" local path2="$2"
# Check if paths exist # Check if paths exist
if [[ ! -e "$path1" || ! -e "$path2" ]]; then if [[ ! -e "$path1" || ! -e "$path2" ]]; then
return 1 return 1
fi fi
# For directories, use find + md5sum to compare recursively # For directories, use find + md5sum to compare recursively
# For files, use md5sum directly # For files, use md5sum directly
if [[ -d "$path1" && -d "$path2" ]]; then if [[ -d "$path1" && -d "$path2" ]]; then
# Compare directory contents using find and md5sum # Compare directory contents using find and md5sum
local hash1=$(find "$path1" -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}') local hash1=$(find "$path1" -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}')
local hash2=$(find "$path2" -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}') local hash2=$(find "$path2" -type f -exec md5sum {} \; | sort -k 2 | md5sum | awk '{print $1}')
[[ "$hash1" == "$hash2" ]] [[ "$hash1" == "$hash2" ]]
elif [[ -f "$path1" && -f "$path2" ]]; then elif [[ -f "$path1" && -f "$path2" ]]; then
# Compare file hashes # Compare file hashes
local hash1=$(md5sum "$path1" | awk '{print $1}') local hash1=$(md5sum "$path1" | awk '{print $1}')
local hash2=$(md5sum "$path2" | awk '{print $1}') local hash2=$(md5sum "$path2" | awk '{print $1}')
[[ "$hash1" == "$hash2" ]] [[ "$hash1" == "$hash2" ]]
else else
# One is a file, one is a directory - different types # One is a file, one is a directory - different types
return 1 return 1
fi fi
} }
# Find next backup number # Find next backup number
get_next_backup_number() { get_next_backup_number() {
local base_path="$1" local base_path="$1"
local counter=1 local counter=1
while [[ -e "${base_path}.old.${counter}" ]]; do while [[ -e "${base_path}.old.${counter}" ]]; do
((counter++)) ((counter++))
done done
echo $counter echo $counter
} }
# ============================================================================= # =============================================================================
@@ -152,33 +162,33 @@ readarray patterns < <(yq -o=j -I=0 '.patterns[]' "$CONFIG_FILE")
# Process each pattern # Process each pattern
for pattern in "${patterns[@]}"; do for pattern in "${patterns[@]}"; do
from=$(echo "$pattern" | yq '.from' - | envsubst) from=$(echo "$pattern" | yq '.from' - | envsubst)
to=$(echo "$pattern" | yq '.to' - | envsubst) to=$(echo "$pattern" | yq '.to' - | envsubst)
mode=$(echo "$pattern" | yq '.mode' - | envsubst) mode=$(echo "$pattern" | yq '.mode' - | envsubst)
condition=$(echo "$pattern" | yq '.condition // "true"') condition=$(echo "$pattern" | yq '.condition // "true"')
# Handle fontconfig fontset override # Handle fontconfig fontset override
# If FONTSET_DIR_NAME is set and this is the fontconfig pattern, use the fontset instead # If FONTSET_DIR_NAME is set and this is the fontconfig pattern, use the fontset instead
if [[ "$from" == "dots/.config/fontconfig" ]] && [[ -n "${FONTSET_DIR_NAME:-}" ]]; then if [[ "$from" == "dots/.config/fontconfig" ]] && [[ -n "${FONTSET_DIR_NAME:-}" ]]; then
from="dots-extra/fontsets/${FONTSET_DIR_NAME}" from="dots-extra/fontsets/${FONTSET_DIR_NAME}"
echo "Using fontset \"${FONTSET_DIR_NAME}\" for fontconfig" echo "Using fontset \"${FONTSET_DIR_NAME}\" for fontconfig"
fi fi
# Check if pattern should be processed # Check if pattern should be processed
if ! should_process_pattern "$pattern"; then if ! should_process_pattern "$pattern"; then
# Format condition message nicely # Format condition message nicely
if [[ "$condition" != "true" ]]; then if [[ "$condition" != "true" ]]; then
cond_type=$(echo "$condition" | yq -r '.type // ""') cond_type=$(echo "$condition" | yq -r '.type // ""')
cond_value=$(echo "$condition" | yq -r '.value // ""') cond_value=$(echo "$condition" | yq -r '.value // ""')
if [[ -n "$cond_type" && -n "$cond_value" ]]; then if [[ -n "$cond_type" && -n "$cond_value" ]]; then
echo "Skipping $from -> $to (condition not met: $cond_type == '$cond_value')" echo "Skipping $from -> $to (condition not met: $cond_type == '$cond_value')"
else
echo "Skipping $from -> $to (condition not met)"
fi
else else
echo "Skipping $from -> $to (condition not met)" echo "Skipping $from -> $to (condition not met)"
fi fi
continue else
echo "Skipping $from -> $to (condition not met)"
fi
continue
fi fi
echo "Processing: $from -> $to (mode: $mode)" echo "Processing: $from -> $to (mode: $mode)"
@@ -186,84 +196,84 @@ for pattern in "${patterns[@]}"; do
# Build exclude arguments for rsync # Build exclude arguments for rsync
excludes=() excludes=()
if echo "$pattern" | yq -e '.excludes' >/dev/null 2>&1; then if echo "$pattern" | yq -e '.excludes' >/dev/null 2>&1; then
while IFS= read -r exclude; do while IFS= read -r exclude; do
excludes+=(--exclude "$exclude") excludes+=(--exclude "$exclude")
done < <(echo "$pattern" | yq -r '.excludes[]') done < <(echo "$pattern" | yq -r '.excludes[]')
fi fi
# Check if source exists # Check if source exists
if [[ ! -e "$from" ]]; then if [[ ! -e "$from" ]]; then
echo "Warning: Source does not exist: $from (skipping)" echo "Warning: Source does not exist: $from (skipping)"
continue continue
fi fi
# Ensure destination directory exists for files # Ensure destination directory exists for files
if [[ -f "$from" ]]; then if [[ -f "$from" ]]; then
v mkdir -p "$(dirname "$to")" v mkdir -p "$(dirname "$to")"
fi fi
# Execute based on mode # Execute based on mode
case $mode in case $mode in
"sync") "sync")
if [[ -d "$from" ]]; then if [[ -d "$from" ]]; then
warning_rsync_delete warning_rsync_delete
v rsync -av --delete "${excludes[@]}" "$from/" "$to/" v rsync -av --delete "${excludes[@]}" "$from/" "$to/"
else else
warning_rsync_normal warning_rsync_normal
# For files, don't use trailing slash and don't use --delete # For files, don't use trailing slash and don't use --delete
v rsync -av "${excludes[@]}" "$from" "$to" v rsync -av "${excludes[@]}" "$from" "$to"
fi fi
;; ;;
"soft") "soft")
warning_rsync_normal warning_rsync_normal
if [[ -d "$from" ]]; then if [[ -d "$from" ]]; then
v rsync -av "${excludes[@]}" "$from/" "$to/" v rsync -av "${excludes[@]}" "$from/" "$to/"
else else
# For files, don't use trailing slash # For files, don't use trailing slash
v rsync -av "${excludes[@]}" "$from" "$to" v rsync -av "${excludes[@]}" "$from" "$to"
fi fi
;; ;;
"hard") "hard")
v cp -r "$from" "$to"
;;
"hard-backup")
if [[ -e "$to" ]]; then
if files_are_same "$from" "$to"; then
echo "Files are identical, skipping backup"
else
backup_number=$(get_next_backup_number "$to")
v mv "$to" "$to.old.$backup_number"
v cp -r "$from" "$to" v cp -r "$from" "$to"
;; fi
"hard-backup") else
if [[ -e "$to" ]]; then v cp -r "$from" "$to"
if files_are_same "$from" "$to"; then fi
echo "Files are identical, skipping backup" ;;
else "soft-backup")
backup_number=$(get_next_backup_number "$to") if [[ -e "$to" ]]; then
v mv "$to" "$to.old.$backup_number" if files_are_same "$from" "$to"; then
v cp -r "$from" "$to" echo "Files are identical, skipping backup"
fi else
else v cp -r "$from" "$to.new"
v cp -r "$from" "$to" fi
fi else
;; v cp -r "$from" "$to"
"soft-backup") fi
if [[ -e "$to" ]]; then ;;
if files_are_same "$from" "$to"; then "skip")
echo "Files are identical, skipping backup" echo "Skipping $from"
else ;;
v cp -r "$from" "$to.new" "skip-if-exists")
fi if [[ -e "$to" ]]; then
else echo "Skipping $from (destination exists)"
v cp -r "$from" "$to" else
fi v cp -r "$from" "$to"
;; fi
"skip") ;;
echo "Skipping $from" *)
;; echo "Unknown mode: $mode"
"skip-if-exists") ;;
if [[ -e "$to" ]]; then
echo "Skipping $from (destination exists)"
else
v cp -r "$from" "$to"
fi
;;
*)
echo "Unknown mode: $mode"
;;
esac esac
done done
##################################################################################### #####################################################################################
-5
View File
@@ -36,11 +36,6 @@ New features (experimental):
see https://github.com/end-4/dots-hyprland/issues/1061 for details. see https://github.com/end-4/dots-hyprland/issues/1061 for details.
${STY_RST}" ${STY_RST}"
} }
# TODO: implement options below for --exp-files
# --exp-files-path <path> Use <path> instead of the default yaml config
# --exp-files-no-strict Ignore error when minor version number is not the same
# --exp-files-regen Force copy the default config to ${EXP_FILE_PATH}
# (auto do this when not existed)
cleancache(){ cleancache(){
rm -rf "${REPO_ROOT}/cache" rm -rf "${REPO_ROOT}/cache"