Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ permissions:
contents: read

env:
# Pinned tool version, kept up to date by Renovate (see renovate.json).
# Pinned tool versions, kept up to date by Renovate (see renovate.json).
# renovate: datasource=github-releases depName=koalaman/shellcheck
SHELLCHECK_VERSION: v0.11.0
# renovate: datasource=github-releases depName=mvdan/sh
SHFMT_VERSION: v3.13.1

Expand All @@ -18,10 +20,14 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

# Scope: actively-maintained scripts only. The Arch installer
# (scripts/install-arch*, scripts/lib/install.sh) is excluded for now and
# tracked to be cleaned up later; scripts/install-arch also fails to parse
# (arithmetic with an embedded [ ] test). shellcheck reads .shellcheckrc.
- name: Install shellcheck
run: |
curl -fsSL \
"https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" \
| tar -xJ -C /tmp
sudo install -m 0755 "/tmp/shellcheck-${SHELLCHECK_VERSION}/shellcheck" /usr/local/bin/shellcheck

# shellcheck reads .shellcheckrc (external-sources + repo-specific disables).
- name: Run shellcheck
run: |
shellcheck --version
Expand All @@ -31,7 +37,11 @@ jobs:
scripts/setup-arch \
scripts/setup-debian \
scripts/setup-fedora \
scripts/install-arch \
scripts/install-arch-backup \
scripts/install-arch-reinstall \
scripts/lib/common.sh \
scripts/lib/install.sh \
config/fedora/snapper/*.sh \
config/fedora/grub-cryptomount/99_cryptomount_check \
vars/arch-vars \
Expand All @@ -50,8 +60,6 @@ jobs:
"https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/shfmt_${SHFMT_VERSION}_linux_amd64"
chmod +x /tmp/shfmt

# scripts/install-arch is excluded: shfmt cannot parse line 498
# (a [ ] test inside $(( )) arithmetic). Everything else is formatted.
- name: Check formatting (-i 4 -ci)
run: |
/tmp/shfmt --version
Expand All @@ -60,6 +68,7 @@ jobs:
install \
scripts/lib/common.sh \
scripts/lib/install.sh \
scripts/install-arch \
scripts/install-arch-backup \
scripts/install-arch-reinstall \
scripts/setup-arch \
Expand Down
6 changes: 5 additions & 1 deletion .shellcheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@
# SC1091 can't follow non-constant 'source "${REPO_ROOT}/..."'
# SC2034 vars/* arrays + cross-file globals look "unused" within one file
# SC2016 intentional literal $VARs in _out '...' install hints / bash -c '...'
# SC2153 UPPER_CASE globals (ROOT_LV/HOME_LV/...) set in install-arch and
# consumed in sourced lib/install.sh look like local-var misspellings
# SC2329 install-arch and its sourced lib/install.sh form one call graph;
# helpers invoked across that boundary are misflagged "never invoked"
external-sources=true
disable=SC1091,SC2034,SC2016
disable=SC1091,SC2034,SC2016,SC2153,SC2329
85 changes: 48 additions & 37 deletions scripts/install-arch
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ _human_readable_size() {
local bytes=$1
numfmt --to=iec-i --suffix=B "$bytes" 2>/dev/null || {
# Fallback if numfmt is not available
echo "$(( bytes / 1024 / 1024 / 1024 ))GB"
echo "$((bytes / 1024 / 1024 / 1024))GB"
}
}

Expand All @@ -85,7 +85,7 @@ _list_available_devices() {
_section "Available Block Devices"

local idx=0
while [ $idx -lt ${#devices[@]} ]; do
while [ "$idx" -lt "${#devices[@]}" ]; do
_out "[$idx] ${devices[$idx]}"
((idx++))
done
Expand All @@ -101,7 +101,7 @@ _prompt_device_selection() {
local device_name

while true; do
read -p "$prompt_msg" selected_device
read -rp "$prompt_msg" selected_device

# Check, if input is an index number
if [[ $selected_device =~ ^[0-9]+$ ]]; then
Expand All @@ -111,7 +111,7 @@ _prompt_device_selection() {
# Change to /dev/xxx format
selected_device="/dev/$device_name"
eval "$device_var=$selected_device"
_info "Selected device: $selected_device (Size: $(_human_readable_size $(_get_device_size "$selected_device")))"
_info "Selected device: $selected_device (Size: $(_human_readable_size "$(_get_device_size "$selected_device")"))"
return 0
else
_error "Invalid index: $selected_device. Please enter a valid index."
Expand All @@ -127,7 +127,7 @@ _prompt_device_selection() {
selected_device="/dev/$selected_device"
fi
eval "$device_var=$selected_device"
_info "Selected device: $selected_device (Size: $(_human_readable_size $(_get_device_size "$selected_device")))"
_info "Selected device: $selected_device (Size: $(_human_readable_size "$(_get_device_size "$selected_device")"))"
return 0
else
_error "Invalid device or index: $selected_device. Please try again."
Expand All @@ -142,46 +142,48 @@ _prompt_device_selection() {
_calculate_available_space() {
local device=$1
local partition_name=$2
local device_size_bytes=$(_get_device_size "$device")
local device_size_gb=$(( device_size_bytes / 1024 / 1024 / 1024 ))
local device_size_bytes
device_size_bytes=$(_get_device_size "$device")
local device_size_gb=$((device_size_bytes / 1024 / 1024 / 1024))
local reserved_space=0

# Reserve boot partitions, if on same device as root
if [ "$partition_name" = "ROOT" ]; then
reserved_space=$(( BOOT_EFI_SIZE + BOOT_SIZE ))
reserved_space=$((BOOT_EFI_SIZE + BOOT_SIZE))
# Only reserve swap, if it's a partition (not LVM)
if [ "$SWAP_TYPE" = "partition" ] && [ -n "$SWAP_SIZE" ]; then
reserved_space=$(( reserved_space + SWAP_SIZE ))
reserved_space=$((reserved_space + SWAP_SIZE))
fi
fi

# Reserve boot partitions, swap, and root, if home is on same device as root
if [ "$partition_name" = "HOME" ] && [ "$HOME_DEVICE" = "$ROOT_DEVICE" ]; then
reserved_space=$(( reserved_space + BOOT_EFI_SIZE + BOOT_SIZE ))
reserved_space=$((reserved_space + BOOT_EFI_SIZE + BOOT_SIZE))
# Only reserve swap, if it's a partition (not LVM)
if [ "$SWAP_TYPE" = "partition" ] && [ -n "$SWAP_SIZE" ]; then
reserved_space=$(( reserved_space + SWAP_SIZE ))
reserved_space=$((reserved_space + SWAP_SIZE))
fi
# Also reserve root partition size, if specified
if [ -n "$ROOT_SIZE" ]; then
reserved_space=$(( reserved_space + ROOT_SIZE ))
reserved_space=$((reserved_space + ROOT_SIZE))
fi
fi

local available_gb=$(( device_size_gb - reserved_space ))
if [ $available_gb -lt 0 ]; then
local available_gb=$((device_size_gb - reserved_space))
if [ "$available_gb" -lt 0 ]; then
available_gb=0
fi

echo $available_gb
echo "$available_gb"
}

_prompt_partition_size() {
local partition_name=$1
local device=$2
local device_size_bytes=$(_get_device_size "$device")
local device_size_human=$(_human_readable_size $device_size_bytes)
local available_gb=$(_calculate_available_space "$device" "$partition_name")
local device_size_bytes device_size_human available_gb
device_size_bytes=$(_get_device_size "$device")
device_size_human=$(_human_readable_size "$device_size_bytes")
available_gb=$(_calculate_available_space "$device" "$partition_name")
local size_value
local size_unit

Expand All @@ -207,7 +209,7 @@ _prompt_partition_size() {
fi
fi

read -p "Enter size (e.g., '100G' for 100GB or 'all' to use all available space): " size_input
read -rp "Enter size (e.g., '100G' for 100GB or 'all' to use all available space): " size_input

if [ "$size_input" = "all" ]; then
eval "${partition_name}_SIZE=all"
Expand Down Expand Up @@ -236,7 +238,7 @@ _get_total_ram() {
local ram_kb
ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
# Convert KB to GB - round to nearest GB
echo $(( (ram_kb + 1048575) / 1048576 ))
echo $(((ram_kb + 1048575) / 1048576))
}

_prompt_swap_type() {
Expand All @@ -249,7 +251,7 @@ _prompt_swap_type() {
_out "Select swap type:"
_out "[1] Swap as partition"
_out "[2] Swap as LVM logical volume"
read -p "Enter choice [1 or 2]: " swap_type
read -rp "Enter choice [1 or 2]: " swap_type

case $swap_type in
1)
Expand All @@ -273,7 +275,7 @@ _prompt_hibernation() {
local response

_out ""
read -p "Do you need hibernation support? (y/n): " response
read -rp "Do you need hibernation support? (y/n): " response

if [[ $response =~ ^[Yy]$ ]]; then
HIBERNATION_ENABLED=true
Expand All @@ -290,37 +292,38 @@ _calculate_sqrt() {
local bit=1

# Find the highest bit
while [ $((bit * bit)) -le $num ]; do
while [ $((bit * bit)) -le "$num" ]; do
bit=$((bit * 2))
done
bit=$((bit / 2))

# Binary search for square root
while [ $bit -gt 0 ]; do
if [ $(((sqrt + bit) * (sqrt + bit))) -le $num ]; then
while [ "$bit" -gt 0 ]; do
if [ $(((sqrt + bit) * (sqrt + bit))) -le "$num" ]; then
sqrt=$((sqrt + bit))
fi
bit=$((bit / 2))
done

echo $sqrt
echo "$sqrt"
}

_calculate_swap_size() {
local ram_gb=$(_get_total_ram)
local ram_gb
local swap_gb
local sqrt_ram

sqrt_ram=$(_calculate_sqrt $ram_gb)
ram_gb=$(_get_total_ram)
sqrt_ram=$(_calculate_sqrt "$ram_gb")

if [ "$HIBERNATION_ENABLED" = true ]; then
# For hibernation: RAM + sqrt(RAM)
swap_gb=$(( ram_gb + sqrt_ram ))
swap_gb=$((ram_gb + sqrt_ram))
_info "Swap size for hibernation: ${swap_gb}GB (RAM: ${ram_gb}GB + sqrt(RAM): ${sqrt_ram}GB)"
else
# For non-hibernation: sqrt(RAM), minimum 2GB
swap_gb=$sqrt_ram
if [ $swap_gb -lt 2 ]; then
if [ "$swap_gb" -lt 2 ]; then
swap_gb=2
fi
_info "Swap size without hibernation: sqrt(RAM): ${swap_gb}GB"
Expand Down Expand Up @@ -363,14 +366,16 @@ _print_configuration_summary() {

# Display root and home partition size even, when all is selected
if [ "$ROOT_SIZE_UNIT" = "all" ]; then
local available_root=$(_calculate_available_space "$ROOT_DEVICE" "ROOT")
local available_root
available_root=$(_calculate_available_space "$ROOT_DEVICE" "ROOT")
root_display="${available_root}GB (all available)"
else
root_display="${ROOT_SIZE}${ROOT_SIZE_UNIT}"
fi

if [ "$HOME_SIZE_UNIT" = "all" ]; then
local available_home=$(_calculate_available_space "$HOME_DEVICE" "HOME")
local available_home
available_home=$(_calculate_available_space "$HOME_DEVICE" "HOME")
home_display="${available_home}GB (all available)"
else
home_display="${HOME_SIZE}${HOME_SIZE_UNIT}"
Expand Down Expand Up @@ -428,7 +433,7 @@ _prompt_format_confirmation() {
_out ""

while true; do
read -p "Do you want to format $device and create new partitions? (yes/no): " response
read -rp "Do you want to format $device and create new partitions? (yes/no): " response

case $response in
yes)
Expand Down Expand Up @@ -495,7 +500,13 @@ _setup_disk_partitions() {
end="100%"
_info "Creating root partition (all remaining space)..."
else
end="$((5 + (swap_on_this_device && [ "$SWAP_TYPE" = "partition" ] ? swap_size : 0) + root_size))${root_unit}"
# Root starts after the 5GB boot area plus any swap partition placed
# on this device; end is that offset plus the requested root size.
local swap_offset=0
if [ "$swap_on_this_device" = true ] && [ "$SWAP_TYPE" = "partition" ]; then
swap_offset=$swap_size
fi
end="$((5 + swap_offset + root_size))${root_unit}"
_info "Creating root partition (${root_size}${root_unit})..."
fi
_echo_run parted -s -a optimal "$device" mkpart root linux "$start" "$end" || return 1
Expand Down Expand Up @@ -538,8 +549,8 @@ _resolve_partition_by_label() {
local label=$2
local part

part=$(lsblk -ln -o NAME,PARTLABEL "$device" 2>/dev/null \
| awk -v label="$label" '$2 == label {print "/dev/" $1; exit}')
part=$(lsblk -ln -o NAME,PARTLABEL "$device" 2>/dev/null |
awk -v label="$label" '$2 == label {print "/dev/" $1; exit}')

if [ -z "$part" ] || [ ! -b "$part" ]; then
_error "Could not resolve partition '$label' on $device"
Expand Down Expand Up @@ -911,7 +922,7 @@ main() {
fi

# Check if internet is available
if ! ping -c 1 8.8.8.8 &> /dev/null; then
if ! ping -c 1 8.8.8.8 &>/dev/null; then
_warn "Internet connection not available. Some features may not work."
fi

Expand Down
16 changes: 8 additions & 8 deletions scripts/lib/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ _reinstall_restore_path() {
}

_verify_reinstall_backup() {
local backup_root required missing=0
local backup_root required missing_required=0

backup_root=$(_reinstall_backup_root)

Expand All @@ -598,11 +598,11 @@ _verify_reinstall_backup() {
for required in etc/pacman.conf etc/mkinitcpio.conf etc/default/grub; do
if [ ! -f "${backup_root}/${required}" ]; then
_warn "Missing backup file: ${required}"
missing=1
missing_required=1
fi
done

if [ "$missing" -eq 1 ]; then
if [ "$missing_required" -eq 1 ]; then
if ! _prompt_yes_no "Some backup files are missing. Continue anyway? (y/n): "; then
return 2
fi
Expand Down Expand Up @@ -648,7 +648,7 @@ _prompt_hostname() {
_section "System Hostname"

while true; do
read -p "Enter hostname: " INSTALL_HOSTNAME
read -rp "Enter hostname: " INSTALL_HOSTNAME

if [[ $INSTALL_HOSTNAME =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
_info "Hostname set to: $INSTALL_HOSTNAME"
Expand All @@ -668,7 +668,7 @@ _prompt_timezone() {
_out ""

while true; do
read -p "Enter timezone [${TIMEZONE}]: " timezone
read -rp "Enter timezone [${TIMEZONE}]: " timezone
timezone=${timezone:-$TIMEZONE}

if [ -f "/usr/share/zoneinfo/${timezone}" ] || [ -f "${MNT}/usr/share/zoneinfo/${timezone}" ]; then
Expand All @@ -689,7 +689,7 @@ _prompt_install_username() {
_out ""

while true; do
read -p "Enter username: " username
read -rp "Enter username: " username

if [[ $username =~ ^[a-z_][a-z0-9_-]*$ ]]; then
INSTALL_USERNAME=$username
Expand Down Expand Up @@ -990,7 +990,7 @@ _prompt_cpu_microcode_package() {
_out "[3] Skip microcode"

while true; do
read -p "Select microcode package [1-3]: " choice
read -rp "Select microcode package [1-3]: " choice

case $choice in
1)
Expand Down Expand Up @@ -1040,7 +1040,7 @@ _prompt_gpu_packages() {
_out "[5] Skip GPU drivers"

while true; do
read -p "Select GPU driver set [1-5]: " choice
read -rp "Select GPU driver set [1-5]: " choice

case $choice in
1)
Expand Down