Test the changes that have been made

This commit is contained in:
Bishoy Ehab
2025-10-18 22:53:52 +03:00
parent 0facd08fa9
commit 1fd328f90a
13 changed files with 383 additions and 260 deletions
+66
View File
@@ -0,0 +1,66 @@
name: Smoke Test - exp-update
on:
push:
paths:
- 'install.sh'
- 'sdata/step/exp-update.sh'
- 'sdata/lib/options-exp-update.sh'
pull_request:
paths:
- 'install.sh'
- 'sdata/step/exp-update.sh'
- 'sdata/lib/options-exp-update.sh'
workflow_dispatch:
jobs:
smoke-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Make install.sh executable
run: chmod +x install.sh
- name: Run smoke test
run: |
echo "Running: ./install.sh exp-update --non-interactive --skip-notice --dry-run -v"
# Capture output and exit code
OUTPUT=$(./install.sh exp-update --non-interactive --skip-notice --dry-run -v 2>&1)
EXIT_CODE=$?
echo "Exit code: $EXIT_CODE"
echo "Output:"
echo "$OUTPUT"
# Check exit code
if [ $EXIT_CODE -ne 0 ]; then
echo "❌ Smoke test failed: Non-zero exit code"
exit 1
fi
# Check for expected strings in output
if ! echo "$OUTPUT" | grep -q "DRY-RUN MODE"; then
echo "❌ Smoke test failed: Missing 'DRY-RUN MODE' in output"
exit 1
fi
if ! echo "$OUTPUT" | grep -q "Detecting Repository Structure"; then
echo "❌ Smoke test failed: Missing 'Detecting Repository Structure' in output"
exit 1
fi
# Check for non-empty Structure line (should contain detected directories)
STRUCTURE_LINE=$(echo "$OUTPUT" | grep "Structure:" | head -1)
if [[ -z "$STRUCTURE_LINE" ]] || [[ "$STRUCTURE_LINE" == "Structure:" ]]; then
echo "❌ Smoke test failed: Structure line is empty or malformed"
echo "Found: $STRUCTURE_LINE"
exit 1
fi
echo "✅ Smoke test passed: All checks successful"
echo "Detected structure: $STRUCTURE_LINE"
+1 -3
View File
@@ -1,8 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
cd "$(dirname "$0")" cd "$(dirname "$0")"
# TODO: Use REPO_ROOT instead of base # Use REPO_ROOT instead of base - when scripts are sourced they do not need export to inherit vars
# Also, when scripts are sourced they do not need export to inherit vars
export base="$(pwd)"
REPO_ROOT="$(pwd)" REPO_ROOT="$(pwd)"
source ./sdata/lib/environment-variables.sh source ./sdata/lib/environment-variables.sh
source ./sdata/lib/functions.sh source ./sdata/lib/functions.sh
+8 -6
View File
@@ -1,6 +1,8 @@
# This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang. # This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang.
# NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file. # NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file.
# shellcheck shell=bash
# The script that use this file should have two lines on its top as follows: # The script that use this file should have two lines on its top as follows:
# cd "$(dirname "$0")" # cd "$(dirname "$0")"
# export base="$(pwd)" # export base="$(pwd)"
@@ -9,7 +11,7 @@ function try { "$@" || sleep 0; }
function v(){ function v(){
echo -e "####################################################" echo -e "####################################################"
echo -e "${STY_BLUE}[$0]: Next command:${STY_RST}" echo -e "${STY_BLUE}[$0]: Next command:${STY_RST}"
echo -e "${STY_GREEN}$@${STY_RST}" echo -e "${STY_GREEN}$*${STY_RST}"
local execute=true local execute=true
if $ask;then if $ask;then
while true;do while true;do
@@ -29,14 +31,14 @@ function v(){
done done
fi fi
if $execute;then x "$@";else if $execute;then x "$@";else
echo -e "${STY_YELLOW}[$0]: Skipped \"$@\"${STY_RST}" echo -e "${STY_YELLOW}[$0]: Skipped \"$*\"${STY_RST}"
fi fi
} }
# When use v() for a defined function, use x() INSIDE its definition to catch errors. # When use v() for a defined function, use x() INSIDE its definition to catch errors.
function x(){ function x(){
if "$@";then local cmdstatus=0;else local cmdstatus=1;fi # 0=normal; 1=failed; 2=failed but ignored if "$@";then local cmdstatus=0;else local cmdstatus=1;fi # 0=normal; 1=failed; 2=failed but ignored
while [ $cmdstatus == 1 ] ;do while [ $cmdstatus == 1 ] ;do
echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$@${STY_RED}\" has failed." echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$*${STY_RED}\" has failed."
echo -e "You may need to resolve the problem manually BEFORE repeating this command." echo -e "You may need to resolve the problem manually BEFORE repeating this command."
echo -e "[Tip] If a certain package is failing to install, try installing it separately in another terminal.${STY_RST}" echo -e "[Tip] If a certain package is failing to install, try installing it separately in another terminal.${STY_RST}"
echo " r = Repeat this command (DEFAULT)" echo " r = Repeat this command (DEFAULT)"
@@ -52,9 +54,9 @@ function x(){
esac esac
done done
case $cmdstatus in case $cmdstatus in
0) echo -e "${STY_BLUE}[$0]: Command \"${STY_GREEN}$@${STY_BLUE}\" finished.${STY_RST}";; 0) echo -e "${STY_BLUE}[$0]: Command \"${STY_GREEN}$*${STY_BLUE}\" finished.${STY_RST}";;
1) echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$@${STY_RED}\" has failed. Exiting...${STY_RST}";exit 1;; 1) echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$*${STY_RED}\" has failed. Exiting...${STY_RST}";exit 1;;
2) echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$@${STY_RED}\" has failed but ignored by user.${STY_RST}";; 2) echo -e "${STY_RED}[$0]: Command \"${STY_GREEN}$*${STY_RED}\" has failed but ignored by user.${STY_RST}";;
esac esac
} }
function showfun(){ function showfun(){
+19 -2
View File
@@ -1,13 +1,20 @@
# Handle args for subcmd: exp-update # Handle args for subcmd: exp-update
# shellcheck shell=bash
showhelp(){ showhelp(){
echo -e "Syntax: $0 exp-update [OPTIONS]... echo -e "Usage: install.sh exp-update [OPTIONS]...
Experimental updating without full reinstall.
Updates dotfiles by syncing configuration files to home directory.
Options: Options:
-f, --force Force check all files even if no new commits -f, --force Force check all files even if no new commits
-p, --packages Enable package checking and building -p, --packages Enable package checking and building
-n, --dry-run Show what would be done without making changes -n, --dry-run Show what would be done without making changes
-v, --verbose Enable verbose output -v, --verbose Enable verbose output
-h, --help Show this help message -h, --help Show this help message
-s, --skip-notice Skip notice about script being untested
--non-interactive Run without prompting for user input
This script updates your dotfiles by: This script updates your dotfiles by:
1. Auto-detecting repository structure (dots/ prefix or direct) 1. Auto-detecting repository structure (dots/ prefix or direct)
@@ -15,12 +22,19 @@ This script updates your dotfiles by:
3. Optionally rebuilding packages (if -p flag is used) 3. Optionally rebuilding packages (if -p flag is used)
4. Syncing configuration files to home directory 4. Syncing configuration files to home directory
5. Updating script permissions 5. Updating script permissions
Ignore file patterns support:
- Exact matches (e.g., 'path/to/file')
- Directory patterns (e.g., 'path/to/dir/')
- Wildcards (e.g., '*.log', 'path/*/file')
- Root-relative patterns (e.g., '/.config')
- Substring matching (prefix with '**', e.g., '**temp' matches any path containing 'temp')
" "
} }
# `man getopt` to see more # `man getopt` to see more
para=$(getopt \ para=$(getopt \
-o hfpnv \ -o hfpnv \
-l help,force,packages,dry-run,verbose,skip-notice \ -l help,force,packages,dry-run,verbose,skip-notice,non-interactive \
-n "$0" -- "$@") -n "$0" -- "$@")
[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 [ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1
##################################################################################### #####################################################################################
@@ -42,6 +56,7 @@ CHECK_PACKAGES=false
DRY_RUN=false DRY_RUN=false
VERBOSE=false VERBOSE=false
SKIP_NOTICE=false SKIP_NOTICE=false
NON_INTERACTIVE=false
eval set -- "$para" eval set -- "$para"
while true ; do while true ; do
@@ -57,6 +72,8 @@ while true ; do
# log_info "Verbose mode enabled" # log_info "Verbose mode enabled"
--skip-notice) SKIP_NOTICE=true;shift;; --skip-notice) SKIP_NOTICE=true;shift;;
# log_warning "Skipping notice about script being untested" # log_warning "Skipping notice about script being untested"
--non-interactive) NON_INTERACTIVE=true;shift;;
# log_info "Non-interactive mode enabled"
## Ending ## Ending
--) break ;; --) break ;;
+1
View File
@@ -1,4 +1,5 @@
# Handle args for subcmd: install # Handle args for subcmd: install
# shellcheck shell=bash
showhelp(){ showhelp(){
echo -e "Syntax: $0 [OPTIONS]... echo -e "Syntax: $0 [OPTIONS]...
+4 -1
View File
@@ -1,6 +1,8 @@
# This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang. # This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang.
# NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file. # NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file.
# shellcheck shell=bash
# The script that use this file should have two lines on its top as follows: # The script that use this file should have two lines on its top as follows:
# cd "$(dirname "$0")" export base="$(pwd)" # cd "$(dirname "$0")" export base="$(pwd)"
showhelp_global(){ showhelp_global(){
@@ -35,11 +37,12 @@ Subcommand:
Subcommand: Subcommand:
exp-update Using experimental update script. exp-update Using experimental update script.
Options for exp-update: Options for exp-update:
-u, --update-force Force check all files even if no new commits (update script) -f, --force Force check all files even if no new commits (update script)
-p, --packages Enable package checking and building (update script) -p, --packages Enable package checking and building (update script)
-n, --dry-run Show what would be done without making changes (update script) -n, --dry-run Show what would be done without making changes (update script)
-v, --verbose Enable verbose output (update script) -v, --verbose Enable verbose output (update script)
--skip-notice Skip warning notice (for experimental scripts) --skip-notice Skip warning notice (for experimental scripts)
--non-interactive Run without prompting for user input
" "
} }
+20 -18
View File
@@ -2,6 +2,8 @@
# This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang. # This is NOT a script for execution, but for loading functions, so NOT need execution permission or shebang.
# NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file. # NOTE that you NOT need to `cd ..' because the `$0' is NOT this file, but the script file which will source this file.
# shellcheck shell=bash
# This file is provided for any distros, mainly non-Arch(based) distros. # This file is provided for any distros, mainly non-Arch(based) distros.
# The script that use this file should have two lines on its top as follows: # The script that use this file should have two lines on its top as follows:
@@ -9,8 +11,8 @@
# export base="$(pwd)" # export base="$(pwd)"
install-agsv1(){ install-agsv1(){
x mkdir -p $base/cache/agsv1 x mkdir -p $REPO_ROOT/cache/agsv1
x cd $base/cache/agsv1 x cd $REPO_ROOT/cache/agsv1
try git init -b main try git init -b main
try git remote add origin https://github.com/Aylur/ags.git try git remote add origin https://github.com/Aylur/ags.git
x git pull origin main && git submodule update --init --recursive x git pull origin main && git submodule update --init --recursive
@@ -20,12 +22,12 @@ install-agsv1(){
x meson setup build # --reconfigure x meson setup build # --reconfigure
x meson install -C build x meson install -C build
x sudo mv /usr/local/bin/ags{,v1} x sudo mv /usr/local/bin/ags{,v1}
x cd $base x cd $REPO_ROOT
} }
install-Rubik(){ install-Rubik(){
x mkdir -p $base/cache/Rubik x mkdir -p $REPO_ROOT/cache/Rubik
x cd $base/cache/Rubik x cd $REPO_ROOT/cache/Rubik
try git init -b main try git init -b main
try git remote add origin https://github.com/googlefonts/rubik.git try git remote add origin https://github.com/googlefonts/rubik.git
x git pull origin main && git submodule update --init --recursive x git pull origin main && git submodule update --init --recursive
@@ -35,12 +37,12 @@ install-Rubik(){
x sudo cp OFL.txt /usr/local/share/licenses/ttf-rubik/LICENSE x sudo cp OFL.txt /usr/local/share/licenses/ttf-rubik/LICENSE
x fc-cache -fv x fc-cache -fv
x gsettings set org.gnome.desktop.interface font-name 'Rubik 11' x gsettings set org.gnome.desktop.interface font-name 'Rubik 11'
x cd $base x cd $REPO_ROOT
} }
install-Gabarito(){ install-Gabarito(){
x mkdir -p $base/cache/Gabarito x mkdir -p $REPO_ROOT/cache/Gabarito
x cd $base/cache/Gabarito x cd $REPO_ROOT/cache/Gabarito
try git init -b main try git init -b main
try git remote add origin https://github.com/naipefoundry/gabarito.git try git remote add origin https://github.com/naipefoundry/gabarito.git
x git pull origin main && git submodule update --init --recursive x git pull origin main && git submodule update --init --recursive
@@ -49,12 +51,12 @@ install-Gabarito(){
x sudo mkdir -p /usr/local/share/licenses/ttf-gabarito/ x sudo mkdir -p /usr/local/share/licenses/ttf-gabarito/
x sudo cp OFL.txt /usr/local/share/licenses/ttf-gabarito/LICENSE x sudo cp OFL.txt /usr/local/share/licenses/ttf-gabarito/LICENSE
x fc-cache -fv x fc-cache -fv
x cd $base x cd $REPO_ROOT
} }
install-OneUI(){ install-OneUI(){
x mkdir -p $base/cache/OneUI4-Icons x mkdir -p $REPO_ROOT/cache/OneUI4-Icons
x cd $base/cache/OneUI4-Icons x cd $REPO_ROOT/cache/OneUI4-Icons
try git init -b main try git init -b main
try git remote add origin https://github.com/end-4/OneUI4-Icons.git try git remote add origin https://github.com/end-4/OneUI4-Icons.git
# try git remote add origin https://github.com/mjkim0727/OneUI4-Icons.git # try git remote add origin https://github.com/mjkim0727/OneUI4-Icons.git
@@ -63,12 +65,12 @@ install-OneUI(){
x sudo cp -r OneUI /usr/local/share/icons x sudo cp -r OneUI /usr/local/share/icons
x sudo cp -r OneUI-dark /usr/local/share/icons x sudo cp -r OneUI-dark /usr/local/share/icons
x sudo cp -r OneUI-light /usr/local/share/icons x sudo cp -r OneUI-light /usr/local/share/icons
x cd $base x cd $REPO_ROOT
} }
install-bibata(){ install-bibata(){
x mkdir -p $base/cache/bibata-cursor x mkdir -p $REPO_ROOT/cache/bibata-cursor
x cd $base/cache/bibata-cursor x cd $REPO_ROOT/cache/bibata-cursor
name="Bibata-Modern-Classic" name="Bibata-Modern-Classic"
file="$name.tar.xz" file="$name.tar.xz"
# Use axel because `curl -O` always downloads a file with 0 byte size, idk why # Use axel because `curl -O` always downloads a file with 0 byte size, idk why
@@ -76,12 +78,12 @@ install-bibata(){
tar -xf $file tar -xf $file
x sudo mkdir -p /usr/local/share/icons x sudo mkdir -p /usr/local/share/icons
x sudo cp -r $name /usr/local/share/icons x sudo cp -r $name /usr/local/share/icons
x cd $base x cd $REPO_ROOT
} }
install-MicroTeX(){ install-MicroTeX(){
x mkdir -p $base/cache/MicroTeX x mkdir -p $REPO_ROOT/cache/MicroTeX
x cd $base/cache/MicroTeX x cd $REPO_ROOT/cache/MicroTeX
try git init -b master try git init -b master
try git remote add origin https://github.com/NanoMichael/MicroTeX.git try git remote add origin https://github.com/NanoMichael/MicroTeX.git
x git pull origin master && git submodule update --init --recursive x git pull origin master && git submodule update --init --recursive
@@ -92,7 +94,7 @@ install-MicroTeX(){
x sudo mkdir -p /opt/MicroTeX x sudo mkdir -p /opt/MicroTeX
x sudo cp ./LaTeX /opt/MicroTeX/ x sudo cp ./LaTeX /opt/MicroTeX/
x sudo cp -r ./res /opt/MicroTeX/ x sudo cp -r ./res /opt/MicroTeX/
x cd $base x cd $REPO_ROOT
} }
install-uv(){ install-uv(){
+2
View File
@@ -1,6 +1,8 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # It's not for directly running.
# shellcheck shell=bash
##################################################################################### #####################################################################################
printf "${STY_CYAN}[$0]: Hi there! Before we start:${STY_RST}\n" printf "${STY_CYAN}[$0]: Hi there! Before we start:${STY_RST}\n"
+2
View File
@@ -1,6 +1,8 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # It's not for directly running.
# shellcheck shell=bash
#################### ####################
# Detect distro # Detect distro
# Helpful link(s): # Helpful link(s):
+4 -2
View File
@@ -1,6 +1,8 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # It's not for directly running.
# shellcheck shell=bash
# TODO: https://github.com/end-4/dots-hyprland/issues/2137 # TODO: https://github.com/end-4/dots-hyprland/issues/2137
function warning_rsync(){ function warning_rsync(){
@@ -179,7 +181,7 @@ warn_files_tests+=(/usr/local/share/licenses/ttf-gabarito)
warn_files_tests+=(/usr/local/share/icons/OneUI{,-dark,-light}) warn_files_tests+=(/usr/local/share/icons/OneUI{,-dark,-light})
warn_files_tests+=(/usr/local/share/icons/Bibata-Modern-Classic) warn_files_tests+=(/usr/local/share/icons/Bibata-Modern-Classic)
warn_files_tests+=(/usr/local/bin/{LaTeX,res}) warn_files_tests+=(/usr/local/bin/{LaTeX,res})
for i in ${warn_files_tests[@]}; do for i in "${warn_files_tests[@]}"; do
echo $i echo $i
test -f $i && warn_files+=($i) test -f $i && warn_files+=($i)
test -d $i && warn_files+=($i) test -d $i && warn_files+=($i)
@@ -223,6 +225,6 @@ if [[ -z "${ILLOGICAL_IMPULSE_VIRTUAL_ENV}" ]]; then
printf "\n${STY_RED}[$0]: \!! Important \!! : Please ensure environment variable ${STY_RST} \$ILLOGICAL_IMPULSE_VIRTUAL_ENV ${STY_RED} is set to proper value (by default \"~/.local/state/quickshell/.venv\"), or Quickshell config will not work. We have already provided this configuration in ~/.config/hypr/hyprland/env.conf, but you need to ensure it is included in hyprland.conf, and also a restart is needed for applying it.${STY_RST}\n" printf "\n${STY_RED}[$0]: \!! Important \!! : Please ensure environment variable ${STY_RST} \$ILLOGICAL_IMPULSE_VIRTUAL_ENV ${STY_RED} is set to proper value (by default \"~/.local/state/quickshell/.venv\"), or Quickshell config will not work. We have already provided this configuration in ~/.config/hypr/hyprland/env.conf, but you need to ensure it is included in hyprland.conf, and also a restart is needed for applying it.${STY_RST}\n"
fi fi
if [[ ! -z "${warn_files[@]}" ]]; then if [[ ${#warn_files[@]} -gt 0 ]]; then
printf "\n${STY_RED}[$0]: \!! Important \!! : Please delete ${STY_RST} ${warn_files[*]} ${STY_RED} manually as soon as possible, since we\'re now using AUR package or local PKGBUILD to install them for Arch(based) Linux distros, and they'll take precedence over our installation, or at least take up more space.${STY_RST}\n" printf "\n${STY_RED}[$0]: \!! Important \!! : Please delete ${STY_RST} ${warn_files[*]} ${STY_RED} manually as soon as possible, since we\'re now using AUR package or local PKGBUILD to install them for Arch(based) Linux distros, and they'll take precedence over our installation, or at least take up more space.${STY_RST}\n"
fi fi
+3 -1
View File
@@ -1,6 +1,8 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # It's not for directly running.
# shellcheck shell=bash
printf 'Hi there!\n' printf 'Hi there!\n'
printf 'This script 1. will uninstall [end-4/dots-hyprland > illogical-impulse] dotfiles\n' printf 'This script 1. will uninstall [end-4/dots-hyprland > illogical-impulse] dotfiles\n'
printf ' 2. will try to revert *mostly everything* installed using install.sh, so it'\''s pretty destructive\n' printf ' 2. will try to revert *mostly everything* installed using install.sh, so it'\''s pretty destructive\n'
@@ -40,7 +42,7 @@ starship.toml
thorium-flags.conf thorium-flags.conf
) )
for i in ${dirs[@]} for i in "${dirs[@]}"
do v rm -rf "$XDG_CONFIG_HOME/$i" do v rm -rf "$XDG_CONFIG_HOME/$i"
done done
+187 -192
View File
@@ -1,15 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# exp-update-tester.sh - Test suite for update.sh # exp-update-tester.sh - Test suite for update.sh (sourced subcommand)
# #
set -euo pipefail set -euo pipefail
# Colors # Colors
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' NC='\033[0m'
TESTS_PASSED=0 TESTS_PASSED=0
@@ -32,22 +30,22 @@ log_fail() {
((TESTS_FAILED++)) ((TESTS_FAILED++))
} }
log_info() { log_error() {
echo -e "${YELLOW}[INFO]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# Setup test environment # Setup test environment
setup_test_env() { setup_test_env() {
local temp_dir local temp_dir
temp_dir=$(mktemp -d -t dotfiles-test.XXXXXX) temp_dir=$(mktemp -d -t dotfiles-test.XXXXXX)
cd "$temp_dir" || { echo "Failed to cd to test directory"; return 1; } cd "$temp_dir" || { echo "Failed to cd to test directory"; return 1; }
git init -q git init -q
git config user.email "test@example.com" git config user.email "test@example.com"
git config user.name "Test User" git config user.name "Test User"
git commit --allow-empty -m "Initial commit" -q git commit --allow-empty -m "Initial commit" -q
echo "$temp_dir" echo "$temp_dir"
} }
@@ -63,10 +61,10 @@ cleanup_test_env() {
run_test() { run_test() {
local test_name="$1" local test_name="$1"
local test_func="$2" local test_func="$2"
# Cleanup before test # Cleanup before test
cleanup_test_env cleanup_test_env
# Run the test # Run the test
if $test_func; then if $test_func; then
echo "$test_name passed" echo "$test_name passed"
@@ -79,18 +77,18 @@ run_test() {
# Test 1: Script exists and is executable # Test 1: Script exists and is executable
test_script_exists() { test_script_exists() {
log_test "Checking if update.sh exists and is executable" log_test "Checking if install.sh exists and is executable"
if [[ ! -f "update.sh" ]]; then if [[ ! -f "install.sh" ]]; then
log_fail "update.sh not found" log_fail "install.sh not found"
return 1 return 1
fi fi
if [[ ! -x "update.sh" ]]; then if [[ ! -x "install.sh" ]]; then
log_fail "update.sh is not executable" log_fail "install.sh is not executable"
return 1 return 1
fi fi
log_pass "Script exists and is executable" log_pass "Script exists and is executable"
return 0 return 0
} }
@@ -98,8 +96,8 @@ test_script_exists() {
# Test 2: Script has no syntax errors # Test 2: Script has no syntax errors
test_syntax() { test_syntax() {
log_test "Checking script syntax" log_test "Checking script syntax"
if bash -n update.sh; then if bash -n install.sh; then
log_pass "No syntax errors found" log_pass "No syntax errors found"
return 0 return 0
else else
@@ -111,8 +109,8 @@ test_syntax() {
# Test 3: Help option works # Test 3: Help option works
test_help_option() { test_help_option() {
log_test "Testing --help option" log_test "Testing --help option"
if ./update.sh --help 2>&1 | grep -q "Usage:"; then if ./install.sh exp-update --help 2>&1 | grep -qiE "(Usage|Options|exp-update)"; then
log_pass "Help option works" log_pass "Help option works"
return 0 return 0
else else
@@ -124,52 +122,39 @@ test_help_option() {
# Test 4: Test repository structure detection (dots/ prefix) # Test 4: Test repository structure detection (dots/ prefix)
test_dots_structure() { test_dots_structure() {
log_test "Testing dots/ prefix structure detection" log_test "Testing dots/ prefix structure detection"
local test_repo local test_repo
test_repo=$(setup_test_env) test_repo=$(setup_test_env)
TEST_DIR="$test_repo" TEST_DIR="$test_repo"
cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; }
mkdir -p dots/.config/test-app mkdir -p dots/.config/test-app
mkdir -p dots/.local/bin mkdir -p dots/.local/bin
echo "test config" > dots/.config/test-app/config.conf echo "test config" > dots/.config/test-app/config.conf
git add . git add .
git commit -m "Add dots structure" -q git commit -m "Add dots structure" -q
cat > test_detection.sh << 'EOF' cat > test_detection.sh << EOF
#!/bin/bash #!/bin/bash
# Mock logging and style functions/variables
log_info() { :; }
log_warning() { :; }
log_error() { :; }
log_success() { :; }
log_header() { :; }
log_die() { echo "ERROR: \$1"; exit 1; }
STY_CYAN="" STY_RST="" STY_YELLOW=""
REPO_ROOT="$1" REPO_ROOT="$1"
detect_repo_structure() { source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
local found_dirs=()
if [[ -d "${REPO_ROOT}/dots/.config" ]]; then
found_dirs+=("dots/.config")
[[ -d "${REPO_ROOT}/dots/.local/bin" ]] && found_dirs+=("dots/.local/bin")
elif [[ -d "${REPO_ROOT}/.config" ]]; then
found_dirs+=(".config")
[[ -d "${REPO_ROOT}/.local/bin" ]] && found_dirs+=(".local/bin")
else
for candidate in "dots/.config" ".config" "dots/.local/bin" ".local/bin"; do
if [[ -d "${REPO_ROOT}/${candidate}" ]]; then
if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then
found_dirs+=("${candidate}")
fi
fi
done
fi
if [[ ${#found_dirs[@]} -eq 0 ]]; then
echo "ERROR" >&2
return 1
fi
echo "${found_dirs[@]}"
}
detect_repo_structure detect_repo_structure
EOF EOF
chmod +x test_detection.sh chmod +x test_detection.sh
result=$(./test_detection.sh "$test_repo") result=$(./test_detection.sh "$test_repo")
if [[ "$result" == *"dots/.config"* ]]; then if [[ "$result" == *"dots/.config"* ]]; then
log_pass "Dots structure detected correctly" log_pass "Dots structure detected correctly"
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
@@ -184,52 +169,39 @@ EOF
# Test 5: Test flat structure detection # Test 5: Test flat structure detection
test_flat_structure() { test_flat_structure() {
log_test "Testing flat structure detection" log_test "Testing flat structure detection"
local test_repo local test_repo
test_repo=$(setup_test_env) test_repo=$(setup_test_env)
TEST_DIR="$test_repo" TEST_DIR="$test_repo"
cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; }
mkdir -p .config/test-app mkdir -p .config/test-app
mkdir -p .local/bin mkdir -p .local/bin
echo "test config" > .config/test-app/config.conf echo "test config" > .config/test-app/config.conf
git add . git add .
git commit -m "Add flat structure" -q git commit -m "Add flat structure" -q
cat > test_detection.sh << 'EOF' cat > test_detection.sh << EOF
#!/bin/bash #!/bin/bash
# Mock logging and style functions/variables
log_info() { :; }
log_warning() { :; }
log_error() { :; }
log_success() { :; }
log_header() { :; }
log_die() { echo "ERROR: \$1"; exit 1; }
STY_CYAN="" STY_RST="" STY_YELLOW=""
REPO_ROOT="$1" REPO_ROOT="$1"
detect_repo_structure() { source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
local found_dirs=()
if [[ -d "${REPO_ROOT}/dots/.config" ]]; then
found_dirs+=("dots/.config")
[[ -d "${REPO_ROOT}/dots/.local/bin" ]] && found_dirs+=("dots/.local/bin")
elif [[ -d "${REPO_ROOT}/.config" ]]; then
found_dirs+=(".config")
[[ -d "${REPO_ROOT}/.local/bin" ]] && found_dirs+=(".local/bin")
else
for candidate in "dots/.config" ".config" "dots/.local/bin" ".local/bin"; do
if [[ -d "${REPO_ROOT}/${candidate}" ]]; then
if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then
found_dirs+=("${candidate}")
fi
fi
done
fi
if [[ ${#found_dirs[@]} -eq 0 ]]; then
echo "ERROR" >&2
return 1
fi
echo "${found_dirs[@]}"
}
detect_repo_structure detect_repo_structure
EOF EOF
chmod +x test_detection.sh chmod +x test_detection.sh
result=$(./test_detection.sh "$test_repo") result=$(./test_detection.sh "$test_repo")
if [[ "$result" == *".config"* ]] && [[ "$result" != *"dots/"* ]]; then if [[ "$result" == *".config"* ]] && [[ "$result" != *"dots/"* ]]; then
log_pass "Flat structure detected correctly" log_pass "Flat structure detected correctly"
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
@@ -283,47 +255,22 @@ EOF
mkdir -p .config mkdir -p .config
mkdir -p secrets mkdir -p secrets
cat > test_ignore.sh << 'EOF' cat > test_ignore.sh << EOF
#!/bin/bash #!/bin/bash
# Mock logging and style functions/variables
log_info() { :; }
log_warning() { :; }
log_error() { :; }
log_success() { :; }
log_header() { :; }
log_die() { echo "ERROR: \$1"; exit 1; }
STY_CYAN="" STY_RST="" STY_YELLOW=""
REPO_ROOT="$1" REPO_ROOT="$1"
UPDATE_IGNORE_FILE="${REPO_ROOT}/.updateignore" UPDATE_IGNORE_FILE="${REPO_ROOT}/.updateignore"
HOME_UPDATE_IGNORE_FILE="/dev/null" HOME_UPDATE_IGNORE_FILE="/dev/null"
# Source the production script to use the real should_ignore function
should_ignore() { source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
local file_path="$1"
local relative_path="${file_path#$HOME/}"
local repo_relative=""
if [[ "$file_path" == "$REPO_ROOT"* ]]; then
repo_relative="${file_path#$REPO_ROOT/}"
fi
for ignore_file in "$UPDATE_IGNORE_FILE" "$HOME_UPDATE_IGNORE_FILE"; do
if [[ -f "$ignore_file" ]]; then
while IFS= read -r pattern || [[ -n "$pattern" ]]; do
[[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue
pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[[ -z "$pattern" ]] && continue
if [[ "$relative_path" == "$pattern" ]] || [[ "$repo_relative" == "$pattern" ]]; then
return 0
fi
if [[ "$relative_path" == $pattern ]] || [[ "$repo_relative" == $pattern ]]; then
return 0
fi
if [[ "$pattern" == */ ]]; then
local dir_pattern="${pattern%/}"
if [[ "$relative_path" == "$dir_pattern"/* ]] || [[ "$repo_relative" == "$dir_pattern"/* ]]; then
return 0
fi
fi
if [[ "$file_path" == *"$pattern"* ]] || [[ "$relative_path" == *"$pattern"* ]]; then
return 0
fi
done <"$ignore_file"
fi
done
return 1
}
test_cases=( test_cases=(
"$REPO_ROOT/app.log:0" "$REPO_ROOT/app.log:0"
@@ -335,7 +282,7 @@ test_cases=(
all_passed=true all_passed=true
for test_case in "${test_cases[@]}"; do for test_case in "${test_cases[@]}"; do
IFS=':' read -r file expected <<< "$test_case" IFS=":" read -r file expected <<< "$test_case"
mkdir -p "$(dirname "$file")" mkdir -p "$(dirname "$file")"
touch "$file" touch "$file"
@@ -376,16 +323,22 @@ EOF
# Test 8: Test safe_read security - COMPLETELY NON-INTERACTIVE # Test 8: Test safe_read security - COMPLETELY NON-INTERACTIVE
test_safe_read_security() { test_safe_read_security() {
log_test "Testing safe_read uses secure assignment (printf -v)" log_test "Testing safe_read uses secure assignment (printf -v)"
# Check that safe_read uses printf -v and not eval local awk_script='/^safe_read() \{/,/^\}/'
if grep -A 10 "safe_read()" update.sh | grep -q "printf -v.*varname"; then local safe_read_function
log_pass "safe_read uses secure printf -v assignment" safe_read_function=$(awk "$awk_script" "$ORIGINAL_DIR/sdata/step/exp-update.sh")
return 0
elif grep -A 10 "safe_read()" update.sh | grep -q "eval.*varname"; then if [[ -z "$safe_read_function" ]]; then
log_fail "safe_read uses vulnerable eval assignment" log_fail "Could not find safe_read function"
return 1 return 1
fi
# Check for secure printf -v assignment and absence of eval
if echo "$safe_read_function" | grep -q "printf -v" && ! echo "$safe_read_function" | grep -q "eval"; then
log_pass "safe_read uses secure printf -v assignment and no eval"
return 0
else else
log_fail "Cannot determine safe_read assignment method" log_fail "safe_read does not use secure assignment or contains eval"
return 1 return 1
fi fi
} }
@@ -393,25 +346,25 @@ test_safe_read_security() {
# Test 9: Test dry-run mode # Test 9: Test dry-run mode
test_dry_run() { test_dry_run() {
log_test "Testing dry-run mode" log_test "Testing dry-run mode"
local test_repo local test_repo
test_repo=$(setup_test_env) test_repo=$(setup_test_env)
TEST_DIR="$test_repo" TEST_DIR="$test_repo"
cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; }
mkdir -p dots/.config/test-app # Copy necessary files for install.sh to run
echo "repo config" > dots/.config/test-app/config.conf cp "$ORIGINAL_DIR/install.sh" .
cp -r "$ORIGINAL_DIR/sdata" .
cp -r "$ORIGINAL_DIR/dots" .
chmod +x install.sh
git add . git add .
git commit -m "Add test config" -q git commit -m "Add test config" -q
cp "$ORIGINAL_DIR/update.sh" . # Use non-interactive mode and check for DRY-RUN marker
chmod +x update.sh ./install.sh exp-update -n --skip-notice --non-interactive 2>&1 | tee dry_run_output.txt
# Use printf to pipe responses automatically
printf "y\ny\n" | ./update.sh -n --skip-notice 2>&1 | tee dry_run_output.txt
if grep -q "DRY-RUN" dry_run_output.txt; then if grep -q "DRY-RUN" dry_run_output.txt; then
log_pass "Dry-run mode detected in output" log_pass "Dry-run mode detected in output"
else else
@@ -419,7 +372,7 @@ test_dry_run() {
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
return 1 return 1
fi fi
if [[ ! -f "${HOME}/.config/test-app/config.conf" ]]; then if [[ ! -f "${HOME}/.config/test-app/config.conf" ]]; then
log_pass "No files created in home during dry-run" log_pass "No files created in home during dry-run"
else else
@@ -428,7 +381,7 @@ test_dry_run() {
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
return 1 return 1
fi fi
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
return 0 return 0
} }
@@ -436,20 +389,20 @@ test_dry_run() {
# Test 10: Test command-line flags # Test 10: Test command-line flags
test_flags() { test_flags() {
log_test "Testing command-line flags" log_test "Testing command-line flags"
# Only test non-interactive flags # Only test non-interactive flags
local flags=("-h" "--help") local flags=("-h" "--help")
local all_passed=true local all_passed=true
for flag in "${flags[@]}"; do for flag in "${flags[@]}"; do
if ./update.sh "$flag" 2>&1 | grep -q -E "(Usage|help)"; then if ./install.sh exp-update "$flag" 2>&1 | grep -qiE "(Usage|Options|exp-update)"; then
log_info "$flag recognized" log_test "$flag recognized"
else else
log_info "$flag not recognized" log_test "$flag not recognized"
all_passed=false all_passed=false
fi fi
done done
if [[ "$all_passed" == true ]]; then if [[ "$all_passed" == true ]]; then
log_pass "Help flags recognized correctly" log_pass "Help flags recognized correctly"
return 0 return 0
@@ -464,11 +417,11 @@ test_shellcheck() {
log_test "Running shellcheck (if available)" log_test "Running shellcheck (if available)"
if ! command -v shellcheck &>/dev/null; then if ! command -v shellcheck &>/dev/null; then
log_info "shellcheck not found, skipping static analysis" log_test "shellcheck not found, skipping static analysis"
return 0 return 0
fi fi
if shellcheck -e SC1090,SC1091,SC2148,SC2034,SC2155,SC2164 update.sh; then if shellcheck -e SC1090,SC1091,SC2148,SC2034,SC2155,SC2164 install.sh; then
log_pass "shellcheck passed" log_pass "shellcheck passed"
return 0 return 0
else else
@@ -477,45 +430,87 @@ test_shellcheck() {
fi fi
} }
# Test 12: Test fresh clone scenario # Test 13: Test ** substring ignore patterns
test_fresh_clone() { test_substring_ignore_patterns() {
log_test "Testing fresh clone scenario" log_test "Testing ** substring ignore pattern matching"
local test_repo local test_repo
test_repo=$(setup_test_env) test_repo=$(setup_test_env)
TEST_DIR="$test_repo" TEST_DIR="$test_repo"
cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; }
mkdir -p .config/test-app
echo "config" > .config/test-app/settings.conf
cat > test_fresh_clone.sh << 'EOF'
#!/bin/bash
has_new_commits() {
if git rev-parse --verify HEAD@{1} &>/dev/null; then
[[ "$(git rev-parse HEAD)" != "$(git rev-parse HEAD@{1})" ]]
else
return 0
fi
}
if has_new_commits; then cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; }
cat > .updateignore << 'EOF'
**temp**
**backup**
**test**
EOF
mkdir -p .config/test-app
mkdir -p temp-backup-dir
mkdir -p .local/share/test-temp
mkdir -p .config/temp-file
cat > test_substring_ignore.sh << EOF
#!/bin/bash
# Mock logging and style functions/variables
log_info() { :; }
log_warning() { :; }
log_error() { :; }
log_success() { :; }
log_header() { :; }
log_die() { echo "ERROR: \$1"; exit 1; }
STY_CYAN="" STY_RST="" STY_YELLOW=""
REPO_ROOT="$1"
UPDATE_IGNORE_FILE="${REPO_ROOT}/.updateignore"
HOME_UPDATE_IGNORE_FILE="/dev/null"
# Source the production script to use the real should_ignore function
source "$ORIGINAL_DIR/sdata/step/exp-update.sh"
test_cases=(
"$REPO_ROOT/temp-backup-dir/file:0"
"$REPO_ROOT/.config/test-app/temp.conf:0"
"$REPO_ROOT/.local/share/test-temp/data:0"
"$REPO_ROOT/.config/temp-file/config:0"
"$REPO_ROOT/normal-config:1"
)
all_passed=true
for test_case in "${test_cases[@]}"; do
IFS=":" read -r file expected <<< "$test_case"
mkdir -p "$(dirname "$file")"
touch "$file"
if should_ignore "$file"; then
result=0
else
result=1
fi
if [[ $result -ne $expected ]]; then
echo "FAIL: $file (expected: $expected, got: $result)"
all_passed=false
fi
done
if [[ "$all_passed" == true ]]; then
echo "PASS" echo "PASS"
else else
echo "FAIL" echo "FAIL"
fi fi
EOF EOF
chmod +x test_fresh_clone.sh chmod +x test_substring_ignore.sh
result=$(./test_fresh_clone.sh) result=$(./test_substring_ignore.sh "$test_repo")
if [[ "$result" == "PASS" ]]; then if [[ "$result" == "PASS" ]]; then
log_pass "Fresh clone scenario handled correctly" log_pass "** substring ignore patterns work correctly"
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
return 0 return 0
else else
log_fail "Fresh clone scenario not handled properly" log_fail "** substring ignore patterns failed"
echo "$result"
cd "$ORIGINAL_DIR" cd "$ORIGINAL_DIR"
return 1 return 1
fi fi
@@ -524,32 +519,32 @@ EOF
# Main test runner # Main test runner
main() { main() {
echo -e "${BLUE}================================${NC}" echo -e "${BLUE}================================${NC}"
echo -e "${BLUE} Update.sh Test Suite${NC}" echo -e "${BLUE} Update.sh Test Suite (Sourced Subcommand)${NC}"
echo -e "${BLUE}================================${NC}\n" echo -e "${BLUE}================================${NC}\n"
if [[ ! -f "update.sh" ]]; then if [[ ! -f "install.sh" ]]; then
log_error "Please run this test from the directory containing update.sh" log_error "Please run this test from the directory containing install.sh"
exit 1 exit 1
fi fi
chmod +x update.sh 2>/dev/null || true chmod +x install.sh 2>/dev/null || true
# Define tests # Define tests
tests=( tests=(
"test_script_exists" "test_script_exists"
"test_syntax" "test_syntax"
"test_help_option" "test_help_option"
"test_dots_structure" "test_dots_structure"
"test_flat_structure" "test_flat_structure"
"test_dots_mapping" "test_dots_mapping"
"test_ignore_patterns" "test_ignore_patterns"
"test_substring_ignore_patterns"
"test_safe_read_security" "test_safe_read_security"
"test_dry_run" "test_dry_run"
"test_flags" "test_flags"
"test_shellcheck" "test_shellcheck"
"test_fresh_clone"
) )
# Run tests # Run tests
for test in "${tests[@]}"; do for test in "${tests[@]}"; do
if $test; then if $test; then
@@ -559,7 +554,7 @@ main() {
fi fi
echo echo
done done
# Summary # Summary
echo -e "${BLUE}================================${NC}" echo -e "${BLUE}================================${NC}"
echo -e "${BLUE} Test Summary${NC}" echo -e "${BLUE} Test Summary${NC}"
@@ -567,7 +562,7 @@ main() {
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}" echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}" echo -e "${RED}Failed: $TESTS_FAILED${NC}"
echo -e "${BLUE}Total: ${#tests[@]}${NC}\n" echo -e "${BLUE}Total: ${#tests[@]}${NC}\n"
if [[ $TESTS_FAILED -eq 0 ]]; then if [[ $TESTS_FAILED -eq 0 ]]; then
echo -e "${GREEN}All tests passed! 🎉${NC}\n" echo -e "${GREEN}All tests passed! 🎉${NC}\n"
exit 0 exit 0
@@ -581,7 +576,7 @@ main() {
cleanup() { cleanup() {
echo "Cleaning up test files..." echo "Cleaning up test files..."
cleanup_test_env cleanup_test_env
rm -f test_detection.sh test_ignore.sh test_safe_read.sh test_fresh_clone.sh dry_run_output.txt 2>/dev/null || true rm -f test_detection.sh test_ignore.sh test_safe_read.sh test_fresh_clone.sh test_substring_ignore.sh dry_run_output.txt 2>/dev/null || true
rm -rf "${HOME}/.config/test-app" 2>/dev/null || true rm -rf "${HOME}/.config/test-app" 2>/dev/null || true
} }
Regular → Executable
+66 -35
View File
@@ -1,6 +1,8 @@
# This script is meant to be sourced. # This script is meant to be sourced.
# It's not for directly running. # It's not for directly running.
# shellcheck shell=bash
##################################################################################### #####################################################################################
# #
# update.sh - Enhanced dotfiles update script # update.sh - Enhanced dotfiles update script
@@ -10,7 +12,12 @@
# - Pull latest commits from remote # - Pull latest commits from remote
# - Rebuild packages if PKGBUILD files changed (user choice) # - Rebuild packages if PKGBUILD files changed (user choice)
# - Handle config file conflicts with user choices # - Handle config file conflicts with user choices
# - Respect .updateignore file for exclusions # - Respect .updateignore file for exclusions with flexible pattern matching:
# - Exact matches (e.g., "path/to/file")
# - Directory patterns (e.g., "path/to/dir/")
# - Wildcards (e.g., "*.log", "path/*/file")
# - Root-relative patterns (e.g., "/.config")
# - Substring matching (prefix with "**", e.g., "**temp" matches any path containing "temp")
# #
set -euo pipefail set -euo pipefail
@@ -47,7 +54,7 @@ detect_repo_structure() {
[[ -d "${REPO_ROOT}/.local/share" ]] && found_dirs+=(".local/share") [[ -d "${REPO_ROOT}/.local/share" ]] && found_dirs+=(".local/share")
else else
# Manual detection of common directories # Manual detection of common directories
for candidate in "dots/.config" ".config" "config" "dots/.local/bin" ".local/bin" "dots/.local/share" ".local/share"; do for candidate in "dots/.config" ".config" "dots/.local/bin" ".local/bin" "dots/.local/share" ".local/share"; do
if [[ -d "${REPO_ROOT}/${candidate}" ]]; then if [[ -d "${REPO_ROOT}/${candidate}" ]]; then
# Avoid duplicates # Avoid duplicates
if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then
@@ -94,8 +101,6 @@ safe_read() {
fi fi
fi fi
} }
# Function to check if a file should be ignored
should_ignore() { should_ignore() {
local file_path="$1" local file_path="$1"
local relative_path="${file_path#$HOME/}" local relative_path="${file_path#$HOME/}"
@@ -161,9 +166,10 @@ should_ignore() {
done done
fi fi
# Simple substring matching (for backward compatibility) # Substring matching (only if pattern starts with '**')
if [[ ! "$should_skip" == true ]]; then if [[ ! "$should_skip" == true && "$pattern" == \*\** ]]; then
if [[ "$file_path" == *"$pattern"* ]] || [[ "$relative_path" == *"$pattern"* ]]; then local substring_pattern="${pattern#\*\*}" # Remove the leading '**'
if [[ -n "$substring_pattern" && ("$file_path" == *"$substring_pattern"* || "$relative_path" == *"$substring_pattern"*) ]]; then
should_skip=true should_skip=true
fi fi
fi fi
@@ -417,7 +423,6 @@ list_packages() {
build_packages() { build_packages() {
local build_mode="$1" local build_mode="$1"
local packages_to_build=() local packages_to_build=()
local rebuilt_packages=0
case "$build_mode" in case "$build_mode" in
"changed") "changed")
@@ -474,7 +479,6 @@ build_packages() {
log_info "Package building cancelled by user" log_info "Package building cancelled by user"
return return
fi fi
for pkg_name in "${packages_to_build[@]}"; do for pkg_name in "${packages_to_build[@]}"; do
pkg_dir="${ARCH_PACKAGES_DIR}/${pkg_name}" pkg_dir="${ARCH_PACKAGES_DIR}/${pkg_name}"
@@ -490,7 +494,10 @@ build_packages() {
continue continue
fi fi
cd "$pkg_dir" || continue cd "$pkg_dir" || {
log_error "Failed to change to package directory: $pkg_dir"
continue
}
if makepkg -si --noconfirm; then if makepkg -si --noconfirm; then
log_success "Successfully built and installed $pkg_name" log_success "Successfully built and installed $pkg_name"
@@ -655,9 +662,20 @@ rebuilt_packages=0
if [[ "$CHECK_PACKAGES" == true ]]; then if [[ "$CHECK_PACKAGES" == true ]]; then
log_header "Package Management" log_header "Package Management"
if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then # Check if required Arch Linux tools are available
log_warning "No packages directory found (tried: dist-arch, arch-packages, sdist/arch). Skipping package management." if ! command -v pacman &>/dev/null || ! command -v makepkg &>/dev/null; then
log_warning "Arch Linux package management tools (pacman/makepkg) not found."
log_warning "Skipping package management as this appears to be a non-Arch Linux system."
log_warning "Use -p/--packages flag only on Arch Linux systems."
PKG_TOOLS_AVAILABLE=false
else else
PKG_TOOLS_AVAILABLE=true
fi
if [[ "$PKG_TOOLS_AVAILABLE" == true ]]; then
if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then
log_warning "No packages directory found (tried: dist-arch, arch-packages, sdist/arch). Skipping package management."
else
changed_pkgbuilds=() changed_pkgbuilds=()
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
@@ -678,7 +696,19 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
echo "4) Skip package building" echo "4) Skip package building"
echo echo
if safe_read "Choose an option (1-4): " pkg_choice "1"; then if [[ "$NON_INTERACTIVE" == true ]]; then
pkg_choice="1"
log_info "Non-interactive mode: Using default package option: $pkg_choice"
elif safe_read "Choose an option (1-4): " pkg_choice "1"; then
if [[ "$VERBOSE" == true ]]; then
log_info "User selected package option: $pkg_choice"
fi
else
log_warning "Failed to read input. Skipping package building."
pkg_choice=""
fi
if [[ -n "$pkg_choice" ]]; then
case $pkg_choice in case $pkg_choice in
1) build_packages "changed" ;; 1) build_packages "changed" ;;
2) 2)
@@ -689,14 +719,23 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
3) build_packages "all" ;; 3) build_packages "all" ;;
4 | *) log_info "Skipping package building" ;; 4 | *) log_info "Skipping package building" ;;
esac esac
else
log_warning "Failed to read input. Skipping package building."
fi fi
else else
log_info "No PKGBUILDs have changed since last update." log_info "No PKGBUILDs have changed since last update."
echo echo
if safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then if [[ "$NON_INTERACTIVE" == true ]]; then
if [[ "$check_anyway" =~ ^[Yy]$ ]]; then check_anyway="N"
log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway"
elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then
if [[ "$VERBOSE" == true ]]; then
log_info "User chose to check packages anyway: $check_anyway"
fi
else
log_warning "Failed to read input. Skipping package management."
check_anyway=""
fi
if [[ -n "$check_anyway" && "$check_anyway" =~ ^[Yy]$ ]]; then
if list_packages; then if list_packages; then
echo echo
echo "Package build options:" echo "Package build options:"
@@ -717,26 +756,11 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
else else
log_info "Skipping package management" log_info "Skipping package management"
fi fi
else
log_info "Skipping package management"
fi fi
fi fi
fi else
else log_header "Package Management"
log_header "Package Management" log_info "Package checking disabled. Use -p or --packages flag to enable package management."
log_info "Package checking disabled. Use -p or --packages flag to enable package management."
if [[ -d "$ARCH_PACKAGES_DIR" ]]; then
changed_count=0
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
if [[ -f "${pkg_dir}/PKGBUILD" ]] && check_pkgbuild_changed "$pkg_dir"; then
((changed_count++))
fi
done
if [[ $changed_count -gt 0 ]]; then
log_warning "Note: $changed_count package(s) have changed PKGBUILDs. Use -p flag to manage packages."
fi
fi fi
fi fi
@@ -794,9 +818,16 @@ if [[ "$process_files" == true ]]; then
home_file="${home_dir_path}/${rel_path}" home_file="${home_dir_path}/${rel_path}"
if should_ignore "$home_file"; then if should_ignore "$home_file"; then
if [[ "$VERBOSE" == true ]]; then
log_info "Ignored: $rel_path (matches ignore pattern)"
fi
continue continue
fi fi
if [[ "$VERBOSE" == true ]]; then
log_info "Processing: $rel_path"
fi
((files_processed++)) ((files_processed++))
if [[ "$DRY_RUN" != true ]]; then if [[ "$DRY_RUN" != true ]]; then