From 731beb0f7caf89d8882836b4db8ff7bfa1871a01 Mon Sep 17 00:00:00 2001 From: clsty Date: Sat, 18 Oct 2025 00:38:49 +0800 Subject: [PATCH] Better integration; introduce subcommand --- install.sh | 43 ++++------ sdata/exp/update.sh | 138 ++++++-------------------------- sdata/lib/functions.sh | 20 +++++ sdata/lib/options-exp-update.sh | 75 +++++++++++++++++ sdata/lib/options-install.sh | 64 +++++++++++++++ sdata/lib/options.sh | 109 +++++++++---------------- 6 files changed, 237 insertions(+), 212 deletions(-) mode change 100755 => 100644 sdata/exp/update.sh create mode 100644 sdata/lib/options-exp-update.sh create mode 100644 sdata/lib/options-install.sh diff --git a/install.sh b/install.sh index 0ac5ee1b5..4e825aefe 100755 --- a/install.sh +++ b/install.sh @@ -1,10 +1,6 @@ #!/usr/bin/env bash cd "$(dirname "$0")" export base="$(pwd)" - -# Store original arguments for experimental scripts -ORIGINAL_ARGS=("$@") - source ./sdata/lib/environment-variables.sh source ./sdata/lib/functions.sh source ./sdata/lib/package-installers.sh @@ -14,28 +10,17 @@ prevent_sudo_or_root set -e ##################################################################################### -# For uninstall script -if [[ "${EXPERIMENTAL_UNINSTALL_SCRIPT}" = true ]]; then - source ./sdata/exp/uninstall.sh - exit -fi -# For update script -if [[ "${EXPERIMENTAL_UPDATE_SCRIPT}" = true ]]; then - export SOURCED_FROM_INSTALL=true - # Pass only update-specific arguments - UPDATE_ARGS=() - for arg in "${ORIGINAL_ARGS[@]}"; do - case "$arg" in - -u|--update-force|-p|--packages|-n|--dry-run|-v|--verbose|--skip-notice) - UPDATE_ARGS+=("$arg") - ;; - *) - ;; - esac - done - bash ./sdata/exp/update.sh "${UPDATE_ARGS[@]}" - exit -fi +# For subcommands +case ${SCRIPT_SUBCOMMAND} in + exp-uninstall) + source ./sdata/exp/uninstall.sh + exit + ;; + exp-update) + source ./sdata/exp/update.sh + exit + ;; +esac ##################################################################################### # 0. Before we start if [[ "${SKIP_ALLGREETING}" != true ]]; then @@ -54,9 +39,9 @@ fi ##################################################################################### if [[ "${SKIP_ALLFILES}" != true ]]; then printf "${STY_CYAN}[$0]: 3. Copying config files\n${STY_RST}" - if [[ "${EXPERIMENTAL_FILES_SCRIPT}" != true ]]; then - source ./sdata/step/3.install-files.sh - else + if [[ "${EXPERIMENTAL_FILES_SCRIPT}" == true ]]; then source ./sdata/step/3.install-files.experimental.sh + else + source ./sdata/step/3.install-files.sh fi fi diff --git a/sdata/exp/update.sh b/sdata/exp/update.sh old mode 100755 new mode 100644 index 53fb81d12..09218cb66 --- a/sdata/exp/update.sh +++ b/sdata/exp/update.sh @@ -1,4 +1,7 @@ -#!/usr/bin/env bash +# This script is meant to be sourced. +# It's not for directly running. + +##################################################################################### # # update.sh - Enhanced dotfiles update script # @@ -11,12 +14,11 @@ # set -euo pipefail -# === Configuration === -FORCE_CHECK=false -CHECK_PACKAGES=false -DRY_RUN=false -VERBOSE=false -REPO_DIR="$(cd "$(dirname "$(dirname "$(dirname "$0")")")" &>/dev/null && pwd)" +REPO_DIR="$(pwd)" + +# TODO: For Arch(-Linux) specific part please check if pacman exists first, if not it should be skipped. + +# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. # Try to find the packages directory (different names in different versions) if [[ -d "${REPO_DIR}/dist-arch" ]]; then ARCH_PACKAGES_DIR="${REPO_DIR}/dist-arch" @@ -30,6 +32,7 @@ fi UPDATE_IGNORE_FILE="${REPO_DIR}/.updateignore" HOME_UPDATE_IGNORE_FILE="${HOME}/.updateignore" +# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. # Auto-detect repository structure detect_repo_structure() { local found_dirs=() @@ -67,41 +70,6 @@ detect_repo_structure() { # Directories to monitor for changes (will be auto-detected) MONITOR_DIRS=() -# === Color Codes === -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -CYAN='\033[0;36m' -PURPLE='\033[0;35m' -NC='\033[0m' # No Color - -# === Helper Functions === -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" >&2 -} - -log_header() { - echo -e "\n${PURPLE}=== $1 ===${NC}" -} - -die() { - log_error "$1" - exit 1 -} - # Function to safely read input with terminal compatibility safe_read() { local prompt="$1" @@ -216,9 +184,9 @@ show_diff() { local file1="$1" local file2="$2" - echo -e "\n${CYAN}Showing differences:${NC}" - echo -e "${CYAN}Old file: $file1${NC}" - echo -e "${CYAN}New file: $file2${NC}" + echo -e "\n${STY_CYAN}Showing differences:${STY_RST}" + echo -e "${STY_CYAN}Old file: $file1${STY_RST}" + echo -e "${STY_CYAN}New file: $file2${STY_RST}" echo "----------------------------------------" if command -v diff &>/dev/null; then @@ -236,7 +204,7 @@ handle_file_conflict() { local filename=$(basename "$home_file") local dirname=$(dirname "$home_file") - echo -e "\n${YELLOW}Conflict detected:${NC} $home_file" + echo -e "\n${STY_YELLOW}Conflict detected:${STY_RST} $home_file" echo "Repository version differs from your local version." echo echo "Choose an action:" @@ -431,17 +399,17 @@ list_packages() { return 1 fi - echo -e "\n${CYAN}Available packages:${NC}" + echo -e "\n${STY_CYAN}Available packages:${STY_RST}" for pkg in "${available_packages[@]}"; do if [[ " ${changed_packages[*]} " =~ " ${pkg} " ]]; then - echo -e " ${GREEN}● ${pkg}${NC} (PKGBUILD changed)" + echo -e " ${STY_GREEN}● ${pkg}${STY_RST} (PKGBUILD changed)" else echo -e " ○ ${pkg}" fi done if [[ ${#changed_packages[@]} -gt 0 ]]; then - echo -e "\n${YELLOW}Packages with changed PKGBUILDs: ${changed_packages[*]}${NC}" + echo -e "\n${STY_YELLOW}Packages with changed PKGBUILDs: ${changed_packages[*]}${STY_RST}" fi return 0 @@ -497,7 +465,7 @@ build_packages() { return fi - echo -e "\n${CYAN}Packages to build: ${packages_to_build[*]}${NC}" + echo -e "\n${STY_CYAN}Packages to build: ${packages_to_build[*]}${STY_RST}" if ! safe_read "Proceed with building these packages? (Y/n): " confirm "Y"; then log_warning "Failed to read input. Skipping package builds." @@ -533,7 +501,7 @@ build_packages() { log_error "Failed to build package $pkg_name" fi - cd "$REPO_DIR" || die "Failed to return to repository directory" + cd "$REPO_DIR" || log_die "Failed to return to repository directory" done if [[ $rebuilt_packages -eq 0 ]]; then @@ -586,63 +554,7 @@ has_new_commits() { # Main script starts here log_header "Dotfiles Update Script" -check=true - -# Parse command line arguments -while [[ $# -gt 0 ]]; do - case $1 in - -f | --force) - FORCE_CHECK=true - log_info "Force check mode enabled - will check all files regardless of git changes" - shift - ;; - -p | --packages) - CHECK_PACKAGES=true - log_info "Package checking enabled" - shift - ;; - -n | --dry-run) - DRY_RUN=true - log_info "Dry-run mode enabled - no changes will be made" - shift - ;; - -v | --verbose) - VERBOSE=true - log_info "Verbose mode enabled" - shift - ;; - -h | --help) - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Options:" - echo " -f, --force Force check all files even if no new commits" - echo " -p, --packages Enable package checking and building" - echo " -n, --dry-run Show what would be done without making changes" - echo " -v, --verbose Enable verbose output" - echo " -h, --help Show this help message" - echo "" - echo "This script updates your dotfiles by:" - echo " 1. Auto-detecting repository structure (dots/ prefix or direct)" - echo " 2. Pulling latest changes from git remote" - echo " 3. Optionally rebuilding packages (if -p flag is used)" - echo " 4. Syncing configuration files to home directory" - echo " 5. Updating script permissions" - exit 0 - ;; - --skip-notice) - log_warning "Skipping notice about script being untested" - check=false - shift - ;; - *) - log_error "Unknown option: $1" - echo "Use --help for usage information" - exit 1 - ;; - esac -done - -if [[ "$check" == true ]]; then +if [[ "$SKIP_NOTICE" != true ]]; then log_warning "THIS SCRIPT IS NOT FULLY TESTED AND MAY CAUSE ISSUES!" log_warning "It might be safer if you want to preserve your modifications and not delete added files," log_warning " but this can cause partial updates and therefore unexpected behavior like in #1856." @@ -656,7 +568,7 @@ if [[ "$check" == true ]]; then fi # Check if we're in a git repository -cd "$REPO_DIR" || die "Failed to change to repository directory" +cd "$REPO_DIR" || log_die "Failed to change to repository directory" if git rev-parse --is-inside-work-tree &>/dev/null; then log_info "Running in git repository: $(git rev-parse --show-toplevel)" @@ -678,7 +590,7 @@ if detected_dirs=$(detect_repo_structure); then fi done else - die "Failed to detect repository structure. Make sure you're in the correct directory." + log_die "Failed to detect repository structure. Make sure you're in the correct directory." fi # Step 1: Pull latest commits @@ -694,7 +606,7 @@ if [[ -z "$current_branch" ]]; then git checkout master current_branch="master" else - die "Could not find main or master branch" + log_die "Could not find main or master branch" fi fi @@ -712,7 +624,7 @@ if ! git diff --quiet || ! git diff --cached --quiet; then fi if [[ ! "$response" =~ ^[Yy]$ ]]; then - die "Aborted by user" + log_die "Aborted by user" fi if [[ "$DRY_RUN" == true ]]; then log_info "[DRY-RUN] Would stash changes" @@ -946,7 +858,7 @@ else fi echo -echo -e "${CYAN}Summary:${NC}" +echo -e "${STY_CYAN}Summary:${STY_RST}" if command -v git >/dev/null && git rev-parse --git-dir >/dev/null 2>&1; then echo "- Repository: $(git log -1 --pretty=format:'%h - %s (%cr)' 2>/dev/null || echo 'Unknown')" else diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index c01b8583a..c9012120d 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -95,3 +95,23 @@ function latest_commit_timestamp(){ fi echo $result } + +function log_info() { + echo -e "${STY_BLUE}[INFO]${STY_RST} $1" +} +function log_success() { + echo -e "${STY_GREEN}[SUCCESS]${STY_RST} $1" +} +function log_warning() { + echo -e "${STY_YELLOW}[WARNING]${STY_RST} $1" +} +function log_error() { + echo -e "${STY_RED}[ERROR]${STY_RST} $1" >&2 +} +function log_header() { + echo -e "\n${STY_PURPLE}=== $1 ===${STY_RST}" +} +function log_die() { + log_error "$1" + exit 1 +} diff --git a/sdata/lib/options-exp-update.sh b/sdata/lib/options-exp-update.sh new file mode 100644 index 000000000..798b6a776 --- /dev/null +++ b/sdata/lib/options-exp-update.sh @@ -0,0 +1,75 @@ +# Handle args for subcmd: exp-update + +showhelp(){ +echo -e "Syntax: $0 exp-update [OPTIONS]... +Options: + -f, --force Force check all files even if no new commits + -p, --packages Enable package checking and building + -n, --dry-run Show what would be done without making changes + -v, --verbose Enable verbose output + -h, --help Show this help message + +This script updates your dotfiles by: + 1. Auto-detecting repository structure (dots/ prefix or direct) + 2. Pulling latest changes from git remote + 3. Optionally rebuilding packages (if -p flag is used) + 4. Syncing configuration files to home directory + 5. Updating script permissions +" +} + 0 + ;; + shift + ;; + *) + log_error "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done +# `man getopt` to see more +para=$(getopt \ + -o hfpnv \ + -l help,force,packages,dry-run,verbose,skip-notice \ + -n "$0" -- "$@") +[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 +##################################################################################### +## getopt Phase 1 +# ignore parameter's order, execute options below first +eval set -- "$para" +while true ; do + case "$1" in + -h|--help) showhelp;exit;; + --) break ;; + *) shift ;; + esac +done +##################################################################################### +## getopt Phase 2 + +FORCE_CHECK=false +CHECK_PACKAGES=false +DRY_RUN=false +VERBOSE=false + +eval set -- "$para" +while true ; do + case "$1" in + ## Ones without parameter + -f|--force) FORCE_CHECK=true;shift;; + # log_info "Force check mode enabled - will check all files regardless of git changes" + -p|--packages) CHECK_PACKAGES=true;shift;; + # log_info "Package checking enabled" + -n|--dry-run) DRY_RUN=true;shift;; + # log_info "Dry-run mode enabled - no changes will be made" + -v|--verbose) VERBOSE=true;shift;; + # log_info "Verbose mode enabled" + --skip-notice) SKIP_NOTICE=true;shift;; + # log_warning "Skipping notice about script being untested" + + ## Ending + --) break ;; + *) echo -e "$0: Wrong parameters.";exit 1;; + esac +done diff --git a/sdata/lib/options-install.sh b/sdata/lib/options-install.sh new file mode 100644 index 000000000..5ca4bea7c --- /dev/null +++ b/sdata/lib/options-install.sh @@ -0,0 +1,64 @@ +# Handle args for subcmd: install +cleancache(){ + rm -rf "$base/cache" +} + +# `man getopt` to see more +para=$(getopt \ + -o hfk:cs \ + -l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-fish,skip-hyprland,skip-plasmaintg,skip-miscconf,exp-files,via-nix \ + -n "$0" -- "$@") +[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 +##################################################################################### +## getopt Phase 1 +# ignore parameter's order, execute options below first +eval set -- "$para" +while true ; do + case "$1" in + -h|--help) showhelp_global;exit;; + -c|--clean) cleancache;shift;; + --) break ;; + *) shift ;; + esac +done +##################################################################################### +## getopt Phase 2 + +eval set -- "$para" +while true ; do + case "$1" in + ## Already processed in phase 1, but not exited + -c|--clean) shift;; + ## Ones without parameter + -f|--force) ask=false;shift;; + --skip-allgreeting) SKIP_ALLGREETING=true;shift;; + --skip-alldeps) SKIP_ALLDEPS=true;shift;; + --skip-allsetups) SKIP_ALLSETUPS=true;shift;; + --skip-allfiles) SKIP_ALLFILES=true;shift;; + -s|--skip-sysupdate) SKIP_SYSUPDATE=true;shift;; + --skip-hyprland) SKIP_HYPRLAND=true;shift;; + --skip-fish) SKIP_FISH=true;shift;; + --skip-miscconf) SKIP_MISCCONF=true;shift;; + --skip-plasmaintg) SKIP_PLASMAINTG=true;shift;; + --exp-files) EXPERIMENTAL_FILES_SCRIPT=true;shift;; + --via-nix) INSTALL_VIA_NIX=true;shift;; + + ## Update script specific options + -u|--update-force) UPDATE_FORCE=true;shift;; + -p|--packages) UPDATE_PACKAGES=true;shift;; + -n|--dry-run) UPDATE_DRY_RUN=true;shift;; + -v|--verbose) UPDATE_VERBOSE=true;shift;; + --skip-notice) SKIP_NOTICE=true;shift;; + + ## Ones with parameter + --fontset) + case $2 in + "default"|"zh-CN"|"vi") fontset="$2";; + *) echo -e "Wrong argument for $1.";exit 1;; + esac;echo "The fontset is ${fontset}.";shift 2;; + + ## Ending + --) break ;; + *) echo -e "$0: Wrong parameters.";exit 1;; + esac +done diff --git a/sdata/lib/options.sh b/sdata/lib/options.sh index 3485d17fe..ea12ad2a3 100644 --- a/sdata/lib/options.sh +++ b/sdata/lib/options.sh @@ -3,12 +3,15 @@ # The script that use this file should have two lines on its top as follows: # cd "$(dirname "$0")" export base="$(pwd)" -showhelp(){ -echo -e "Syntax: $0 [Options]... +showhelp_global(){ +echo -e "Syntax: $0 [subcommand] [options]... Idempotent installation script for dotfiles. -If no option is specified, run default install process. +If no option nor subcommand is specified, run default install process. +Subcommand: + install The default subcommand which can be omitted. +Options for install: -h, --help Print this help message and exit -f, --force (Dangerous) Force mode without any confirm -c, --clean Clean the build cache first @@ -26,9 +29,13 @@ If no option is specified, run default install process. --fontset (Unavailable yet) Use a set of pre-defined font and config --via-nix (Unavailable yet) Use Nix to install dependencies --exp-uninstall Use experimental uninstall script - --exp-update Use experimental update script - -Update Script Options (only with --exp-update): + +Subcommand: + exp-uninstall Using experimental uninstall script. + +Subcommand: + exp-update Using experimental update script. +Options for exp-update: -u, --update-force Force check all files even if no new commits (update script) -p, --packages Enable package checking and building (update script) -n, --dry-run Show what would be done without making changes (update script) @@ -37,68 +44,30 @@ Update Script Options (only with --exp-update): " } -cleancache(){ - rm -rf "$base/cache" -} +# Handle subcommand +case $1 in + # subcommand specified + install|exp-uninstall|exp-update) + SCRIPT_SUBCOMMAND=$1 + shift + ;; + # no subcommand (has options: -* ; no options: "") + -*|"") + SCRIPT_SUBCOMMAND=install + ;; + # wrong subcommand + *)echo "Unknown subcommand \"$1\", aborting...";exit 1;; +esac -# `man getopt` to see more -para=$(getopt \ - -o hfk:csu:p:n:v \ - -l help,force,fontset:,clean,skip-allgreeting,skip-alldeps,skip-allsetups,skip-allfiles,skip-sysupdate,skip-fish,skip-hyprland,skip-plasmaintg,skip-miscconf,exp-files,via-nix,exp-uninstall,exp-update,update-force,packages,dry-run,verbose,skip-notice \ - -n "$0" -- "$@") -[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 -##################################################################################### -## getopt Phase 1 -# ignore parameter's order, execute options below first -eval set -- "$para" -while true ; do - case "$1" in - -h|--help) showhelp;exit;; - -c|--clean) cleancache;shift;; - --) break ;; - *) shift ;; - esac -done -##################################################################################### -## getopt Phase 2 - -eval set -- "$para" -while true ; do - case "$1" in - ## Already processed in phase 1, but not exited - -c|--clean) shift;; - ## Ones without parameter - -f|--force) ask=false;shift;; - --skip-allgreeting) SKIP_ALLGREETING=true;shift;; - --skip-alldeps) SKIP_ALLDEPS=true;shift;; - --skip-allsetups) SKIP_ALLSETUPS=true;shift;; - --skip-allfiles) SKIP_ALLFILES=true;shift;; - -s|--skip-sysupdate) SKIP_SYSUPDATE=true;shift;; - --skip-hyprland) SKIP_HYPRLAND=true;shift;; - --skip-fish) SKIP_FISH=true;shift;; - --skip-miscconf) SKIP_MISCCONF=true;shift;; - --skip-plasmaintg) SKIP_PLASMAINTG=true;shift;; - --exp-files) EXPERIMENTAL_FILES_SCRIPT=true;shift;; - --via-nix) INSTALL_VIA_NIX=true;shift;; - --exp-uninstall) EXPERIMENTAL_UNINSTALL_SCRIPT=true;shift;; - --exp-update) EXPERIMENTAL_UPDATE_SCRIPT=true;shift;; - - ## Update script specific options - -u|--update-force) UPDATE_FORCE=true;shift;; - -p|--packages) UPDATE_PACKAGES=true;shift;; - -n|--dry-run) UPDATE_DRY_RUN=true;shift;; - -v|--verbose) UPDATE_VERBOSE=true;shift;; - --skip-notice) SKIP_NOTICE=true;shift;; - - ## Ones with parameter - --fontset) - case $2 in - "default"|"zh-CN"|"vi") fontset="$2";; - *) echo -e "Wrong argument for $1.";exit 1;; - esac;echo "The fontset is ${fontset}.";shift 2;; - - ## Ending - --) break ;; - *) echo -e "$0: Wrong parameters.";exit 1;; - esac -done +# Handle options for subcommand +case ${SCRIPT_SUBCOMMAND} in + install) + source ./sdata/lib/options-install.sh + ;; + exp-uninstall) + #source ./sdata/lib/options-exp-uninstall.sh + ;; + exp-update) + source ./sdata/lib/options-exp-update.sh + ;; +esac