Skip to content
131 changes: 79 additions & 52 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install Shellcheck
run: sudo apt-get install -y shellcheck
- name: Install Shellcheck & Zsh
run: sudo apt-get install -y shellcheck zsh

- name: Lint Installer (Bash)
# SC2086: Double quote variables (ignored for flexibility)
# SC2034: Unused variables (ignored)
# SC1091: Not following sourced files (ignored for /etc/os-release)
run: shellcheck -e SC2086,SC2034,SC1091 install.sh
run: shellcheck install.sh

- name: Verify Zsh Syntax
run: |
sudo apt-get install -y zsh
zsh -n .zshrc
- name: Lint Theme Switcher (Bash)
run: shellcheck theme.sh

- name: Lint Uninstaller (Bash)
run: shellcheck uninstall.sh

- name: Verify Zsh Syntax (.zshrc)
run: zsh -n .zshrc

# -----------------------------------------------------------------------------
# JOB 2: INTEGRATION TEST (UBUNTU & MACOS)
Expand All @@ -44,19 +45,17 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Grant Execute Permissions
run: chmod +x install.sh

- name: Run Installer (Non-Interactive)
# Pipes 'yes' for prompts. Uses CI_ENV to skip fonts/chsh.
run: |
yes | ./install.sh
run: yes | ./install.sh
env:
CI_ENV: true
CI_ENV: true

- name: Verify Zshrc Created
run: |
Expand All @@ -65,17 +64,38 @@ jobs:
exit 1
fi

# FIX FOR MAC/UBUNTU:
# We must run zsh once to trigger the Zinit git clone inside .zshrc
- name: Bootstrap Zinit (Initialize Zsh)
run: zsh -ic "exit" || true
run: |
TERM=xterm-256color zsh -ic "exit" 2>&1 || echo "Note: zsh bootstrap exited non-zero (likely async plugin load — see output above)"

- name: Verify Plugins Installed
- name: Verify Zinit Installed
run: |
# Check standard XDG path or legacy path
if [ ! -d "${HOME}/.local/share/zinit" ] && [ ! -d "${HOME}/.zinit" ]; then
echo "Error: Zinit directory not found! Plugin installation failed."
exit 1
echo "Error: Zinit directory not found! Plugin installation failed."
exit 1
fi

ZINIT_PLUGINS="${HOME}/.local/share/zinit/plugins"
MISSING=0
# "dir:binary" pairs — no associative arrays, compatible with bash 3 (macOS)
for spec in \
"eza-community---eza:eza" \
"sharkdp---bat:bat" \
"junegunn---fzf:fzf" \
"ajeetdsouza---zoxide:zoxide" \
"starship---starship:starship"; do
dir="${spec%%:*}"
bin="${spec##*:}"
if find "$ZINIT_PLUGINS/$dir" -maxdepth 1 -name "$bin" -type f 2>/dev/null | grep -q .; then
echo "PASS: $bin found in Zinit store"
else
echo "WARN: $bin not found ($ZINIT_PLUGINS/$dir)"
MISSING=$((MISSING + 1))
fi
done
if [ "$MISSING" -ge 3 ]; then
echo "Error: Too many tools missing from Zinit store"
exit 1
fi

# -----------------------------------------------------------------------------
Expand All @@ -88,77 +108,84 @@ jobs:
strategy:
matrix:
distro: [archlinux:latest, fedora:latest]

steps:
- uses: actions/checkout@v4

- name: Test in Docker
run: |
docker run --rm -v ${{ github.workspace }}:/workspace -w /workspace ${{ matrix.distro }} /bin/bash -c "
# Set colors for better logging
GREEN='\033[0;32m'
NC='\033[0m'

echo -e \"\${GREEN}>>> 1. SETUP: Installing dependencies for ${{ matrix.distro }}...\${NC}\"

# --- ARCH LINUX ---
if [ -f /etc/arch-release ]; then
pacman -Sy --noconfirm archlinux-keyring
# Ensure wget is installed to handle the file:// download
pacman -Syu --noconfirm git curl wget unzip sudo which base-devel ca-certificates

# 'file' required by Zinit ziextract for archive type detection
pacman -Syu --noconfirm git curl wget unzip sudo which base-devel ca-certificates file
useradd -m -s /bin/bash tester
echo 'tester ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
fi

# --- FEDORA ---
if [ -f /etc/fedora-release ]; then
dnf upgrade -y --refresh
dnf install -y git curl wget unzip sudo util-linux-user which findutils dnf-plugins-core ca-certificates

# Pre-install Eza
# 'file' required by Zinit ziextract — without it every gh-r download
# (bat, fzf, fd, rg, zoxide) fails mid-extract with 'command not found: file'
dnf install -y git curl wget unzip sudo util-linux-user which findutils dnf-plugins-core ca-certificates file
dnf install -y eza || (dnf copr enable -y piegames/eza && dnf install -y eza)

useradd -m -s /bin/bash tester
echo 'tester ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
fi

echo -e \"\${GREEN}>>> 2. PREPARE: Injecting local file path...\${NC}\"

# 1. Copy the repository files to a temp location so we can modify them freely

cp /workspace/install.sh /tmp/install.sh
cp /workspace/.zshrc /tmp/.zshrc

# 2. USE SED TO REPLACE THE URL
# This changes the URL from 'https://...' to 'file:///tmp/.zshrc'
# Now wget will 'download' the local file instead of going to the internet.
sed -i 's|^ZSHRC_URL=.*|ZSHRC_URL=\"file:///tmp/.zshrc\"|' /tmp/install.sh

# 3. Permissions
chmod +x /tmp/install.sh
chmod 644 /tmp/.zshrc
chown -R tester:tester /tmp

echo -e \"\${GREEN}>>> 3. TEST: Running installer as user tester...\${NC}\"

# Run the modified script in /tmp

su tester -c 'export CI_ENV=true && yes | /tmp/install.sh'

echo -e \"\${GREEN}>>> 4. VERIFY: Checking results...\${NC}\"

if [ -f /home/tester/.zshrc ]; then
echo 'SUCCESS: .zshrc created'
else
echo 'FAILURE: .zshrc missing'
exit 1
fi
# Verify Zinit bootstrap
su tester -c 'zsh -ic exit' || true

su tester -c 'TERM=xterm-256color zsh -ic exit' 2>&1 || echo 'Note: zsh bootstrap exited non-zero'

if [ -d /home/tester/.local/share/zinit ]; then
echo 'SUCCESS: Zinit installed'
echo 'SUCCESS: Zinit installed'
else
echo 'FAILURE: Zinit missing'
exit 1
echo 'FAILURE: Zinit missing'
exit 1
fi

echo -e \"\${GREEN}>>> 5. VERIFY: Checking Zinit plugin store...\${NC}\"
ZINIT_PLUGINS=\"/home/tester/.local/share/zinit/plugins\"
MISSING=0
for spec in \"eza-community---eza:eza\" \"starship---starship:starship\" \"sharkdp---bat:bat\" \"junegunn---fzf:fzf\"; do
dir=\"\${spec%%:*}\"
bin=\"\${spec##*:}\"
if find \"\$ZINIT_PLUGINS/\$dir\" -maxdepth 1 -name \"\$bin\" -type f 2>/dev/null | grep -q .; then
echo \"SUCCESS: \$bin found in Zinit store\"
else
echo \"WARN: \$bin not found (\$ZINIT_PLUGINS/\$dir)\"
MISSING=\$((MISSING + 1))
fi
done
if [ \"\$MISSING\" -ge 3 ]; then
echo 'FAILURE: Too many tools missing from Zinit store'
exit 1
fi
"
12 changes: 12 additions & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# .shellcheckrc: Shared Shellcheck configuration for Z-Shift
# FIX #12: Centralises ignore rules so CI (ci.yml) and local tests
# (tests/local_test.sh) use identical linting settings without duplication.

# SC2086: Double-quoting variables — suppressed for flexibility in install_pkg
disable=SC2086

# SC2034: Variable appears unused — suppressed for sourced env vars
disable=SC2034

# SC1091: Not following sourced files — suppressed for /etc/os-release
disable=SC1091
23 changes: 22 additions & 1 deletion .zshrc
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ alias zini='zinit'
alias zup='zinit self-update && zinit update --parallel && zinit cclear && tldr --update'
alias zclean='zinit cclear && zinit delete --clean'

# This avoids a noisy warning if the aliases were never set.

(( ${+aliases[zi]} )) && unalias zi
(( ${+aliases[zpl]} )) && unalias zpl
(( ${+aliases[zplg]})) && unalias zplg

# --- Eza (The ls replacement) ---
if [[ -n "${commands[eza]}" ]]; then
alias ls='eza --icons --group-directories-first --git'
Expand Down Expand Up @@ -200,7 +206,7 @@ zshift-update() {
DATE_STAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$HOME/.zshrc.zshift_${DATE_STAMP}.bak"
TEMP_ZSHRC="$(mktemp)"

UPDATE_URL="${ZSHIFT_CUSTOM_URL:-https://raw.githubusercontent.com/0xdilshan/Z-SHIFT/main/.zshrc}"

echo -e "${BLUE}:: Initiating Z-Shift Update...${NC}"
Expand All @@ -223,6 +229,19 @@ zshift-update() {
cp "$HOME/.zshrc" "$BACKUP_FILE"
fi

# Warn the user if their current .zshrc differs from the backup,
if [ -f "$BACKUP_FILE" ] && ! diff -q "$HOME/.zshrc" "$BACKUP_FILE" > /dev/null 2>&1; then
echo -e "${YELLOW}!! Warning: Your .zshrc has local modifications.${NC}"
echo -e "${YELLOW} These will be replaced. Move personal config to ~/.zshrc.local to preserve it.${NC}"
echo -ne "${YELLOW} Continue anyway? [y/N]: ${NC}"
read -r REPLY
if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
echo -e "${BLUE}:: Update cancelled.${NC}"
rm -f "$TEMP_ZSHRC"
return 0
fi
fi

mv "$TEMP_ZSHRC" "$HOME/.zshrc"
echo -e "${GREEN}:: Configuration file updated.${NC}"

Expand All @@ -241,6 +260,8 @@ zshift-update() {
exec zsh
}

alias zsu='zshift-update'

# =============================================================================
# 7. BYTE-COMPILATION & LOCAL CUSTOMIZATIONS
# =============================================================================
Expand Down
Loading