Compare commits

...
3 Commits
Author SHA1 Message Date
Puechberty Arthur 8410019d78 .install.sh final work ! 2026-06-24 00:41:46 +02:00
Puechberty Arthur c492e21337 fix many error in .install.sh script 2026-06-24 00:15:34 +02:00
Puechberty Arthur 2f33d9f69b update install script 2026-06-23 23:25:22 +02:00
+400 -108
View File
@@ -1,144 +1,436 @@
#!/bin/bash
set -e
#!/usr/bin/env bash
set -euo pipefail
clear
echo "========================"
echo " Arch Custom Installer"
echo "========================"
# ==========================================
# UI SYSTEM
# ==========================================
declare -A GRID_DATA
GRID_NUM_PAGES=0
# =========================
# 1. KEYBOARD
# =========================
echo "Clavier (fr / us / uk / de / es) :"
read -r KEYMAP
loadkeys "$KEYMAP"
grid_menu() {
local -n items=$1
local page=$2
local max_cols=$3
local max_rows=$4
local data_page=0
local item_number=0
local max_cols_data
local line
# =========================
# 2. NETWORK (AUTO + WIFI MANUEL)
# =========================
if ! ping -c 1 archlinux.org &>/dev/null; then
echo "Pas d'internet détecté."
GRID_DATA=()
GRID_NUM_PAGES=0
iwctl device list
echo "Device Wi-Fi (ex: wlan0) :"
read -r DEVICE
while (( item_number < ${#items[@]} )); do
max_cols_data=1
for (( line=0; line<max_rows; line++ )); do
if (( item_number >= ${#items[@]} )); then break; fi
local key="P${data_page}L${line}"
if [[ ! -v GRID_DATA[$key] ]]; then GRID_DATA[$key]=""; fi
iwctl station "$DEVICE" scan
sleep 3
local index=$(( item_number + max_rows - line ))
if (( index >= ${#items[@]} )); then index=$(( ${#items[@]} - 1 ))
elif (( index < 0 )); then index=$(( ${#items[@]} + index ))
(( index < 0 )) && index=0
fi
iwctl station "$DEVICE" get-networks
local index_str; printf -v index_str "%d" "$index"
local item_num_plus1=$(( item_number + 1 ))
local item_num_str; printf -v item_num_str "%d" "$item_num_plus1"
local spaces_needed=$(( ${#index_str} - ${#item_num_str} ))
if (( spaces_needed < 0 )); then spaces_needed=0; fi
local spaces; printf -v spaces "%*s" "$spaces_needed" ""
local item="${items[$item_number]}"
local add="${spaces}${item_num_plus1}) ${item}"
echo "SSID :"
read -r SSID
local current_line="${GRID_DATA[$key]}"
if (( ${#current_line} + ${#add} > max_cols )); then
data_page=$(( data_page + 1 ))
break
fi
GRID_DATA[$key]="${current_line}${add}"
local len_item_plus2=$(( ${#item} + 2 ))
max_cols_data=$(( max_cols_data > len_item_plus2 ? max_cols_data : len_item_plus2 ))
item_number=$(( item_number + 1 ))
done
echo "Password Wi-Fi :"
read -rs PASS
echo
for (( line=0; line<max_rows; line++ )); do
local key="P${data_page}L${line}"
if [[ ! -v GRID_DATA[$key] ]]; then continue; fi
local idx=$(( item_number - (max_rows - line) ))
if (( idx < 0 )); then idx=$(( ${#items[@]} + idx ))
(( idx < 0 )) && idx=0
elif (( idx >= ${#items[@]} )); then idx=$(( ${#items[@]} - 1 ))
fi
local item="${items[$idx]}"
local pad=$(( max_cols_data - ${#item} ))
if (( pad < 0 )); then pad=0; fi
local spaces_pad; printf -v spaces_pad "%*s" "$pad" ""
GRID_DATA[$key]="${GRID_DATA[$key]}${spaces_pad}"
done
done
GRID_NUM_PAGES=$(( data_page + 1 ))
}
iwctl station "$DEVICE" connect "$SSID" --passphrase "$PASS"
print_grid_menu() {
local page=$1
local found=0
local key
for key in "${!GRID_DATA[@]}"; do
if [[ $key == P${page}L* ]]; then found=1; break; fi
done
if (( found == 0 )); then echo "Page $(( page + 1 )) does not exist."; return; fi
sleep 5
fi
echo ""
echo "Page $(( page + 1 )) of $GRID_NUM_PAGES"
echo ""
for key in $(printf "%s\n" "${!GRID_DATA[@]}" | grep "^P${page}L" | sort -t 'L' -k2 -n); do
echo "${GRID_DATA[$key]}"
done
}
# =========================
# 3. TIMEZONE
# =========================
echo "Fuseau horaire (ex: Europe/Paris) :"
read -r TIMEZONE
ask_confirm() { read -rp "$1 (y/n): " ans; [[ "$ans" =~ ^[yY]$ ]]; }
# =========================
# 4. LOCALE
# =========================
echo "Langue système (fr_FR.UTF-8 / en_US.UTF-8 / etc) :"
read -r LOCALE
# Fonction pour échapper les caractères spéciaux JSON (Remplace jq)
json_escape() {
local s="$1"
s="${s//\\/\\\\}" # Antislash
s="${s//\"/\\\"}" # Guillemets
s="${s//$'\n'/\\n}" # Retour à la ligne
s="${s//$'\r'/\\r}" # Retour chariot
s="${s//$'\t'/\\t}" # Tabulation
printf '%s' "$s"
}
# =========================
# 5. USER
# =========================
DEFAULT_USER="user"
echo "Nom utilisateur [$DEFAULT_USER] :"
read -r USERNAME
USERNAME=${USERNAME:-$DEFAULT_USER}
# ==========================================
# SYSTEM LOGIC
# ==========================================
echo "Mot de passe utilisateur :"
read -rs USERPASS
echo
cleanup_install_disk() {
local disk="$1"
[[ -z "$disk" || ! -b "$disk" ]] && return 1
echo "Cleaning up disk: $disk"
findmnt -R /mnt >/dev/null && umount -R /mnt || true
while read -r dev; do
[[ -b "$dev" ]] || continue
swapoff "$dev" 2>/dev/null || true
while read -r target; do [[ -n "$target" ]] && umount "$target" 2>/dev/null; done < <(findmnt -rn -S "$dev" -o TARGET 2>/dev/null)
done < <(lsblk -rnpo PATH "$disk" 2>/dev/null)
while read -r dev type; do
[[ "$type" == "disk" || "$type" == "part" || "$type" == "crypt" ]] || continue
while read -r vg; do [[ -n "$vg" ]] && vgchange -an "$vg" 2>/dev/null; done < <(pvs --noheadings -o vg_name "$dev" 2>/dev/null | awk '{$1=$1; print}' | sort -u)
done < <(lsblk -rnpo PATH,TYPE "$disk" 2>/dev/null)
while read -r dev type; do [[ "$type" == "crypt" ]] && cryptsetup close "$dev" 2>/dev/null; done < <(lsblk -rnpo PATH,TYPE "$disk" 2>/dev/null)
blockdev --flushbufs "$disk" 2>/dev/null || true
partprobe "$disk" 2>/dev/null || true
udevadm settle 2>/dev/null || true
}
# =========================
# 6. HOSTNAME
# =========================
DEFAULT_HOST="archbox"
echo "Hostname [$DEFAULT_HOST] :"
read -r HOSTNAME
HOSTNAME=${HOSTNAME:-$DEFAULT_HOST}
install_base_system() {
pacman-key --init
pacman-key --populate archlinux
pacman -Sy --noconfirm
# =========================
# 7. DISK
# =========================
lsblk -dpno NAME,SIZE,MODEL
local disk
# On lit le disque directement avec bash au lieu de jq
disk=$(grep -o '"device": *"[^"]*"' user_configuration.json | head -1 | cut -d'"' -f4)
cleanup_install_disk "$disk"
echo "Disque (ex: /dev/sda ou /dev/nvme0n1) :"
read -r DISK
archinstall --config user_configuration.json --creds user_credentials.json --silent --skip-ntp --skip-wkd --skip-wifi-check
echo "⚠️ TOUT SERA EFFACÉ SUR $DISK"
echo "Taper YES pour confirmer :"
read -r CONFIRM
[[ "$CONFIRM" != "YES" ]] && exit 1
mkdir -p /mnt/etc/sudoers.d
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /mnt/etc/sudoers.d/99-installer
chmod 440 /mnt/etc/sudoers.d/99-installer
}
# =========================
# 8. PARTITIONING
# =========================
sgdisk --zap-all "$DISK"
# ==========================================
# CONFIGURATOR
# ==========================================
sgdisk -n 1:0:+512M -t 1:ef00 -c 1:"EFI" "$DISK"
sgdisk -n 2:0:0 -t 2:8300 -c 2:"ROOT" "$DISK"
run_configurator() {
# --- 1. KEYBOARD ---
KB_LAYOUT=("us" "fr" "de" "uk" "es" "it" "pt-latin1" "br-abnt2" "dvorak" "colemak" "ru" "jp106")
PAGE=1
COLS=80
ROWS=15
EFI="${DISK}1"
ROOT="${DISK}2"
grid_menu KB_LAYOUT "$PAGE" "$COLS" "$ROWS"
mkfs.fat -F32 "$EFI"
mkfs.ext4 -F "$ROOT"
while true; do
clear
echo "=== SELECT KEYBOARD LAYOUT ===" # TITRE
print_grid_menu $(( PAGE - 1 ))
echo -e "\n[n] Next | [p] Prev | [q] Quit"
read -rp "Entrée (n/p ou nombre) : " input
mount "$ROOT" /mnt
mount --mkdir "$EFI" /mnt/boot
if [[ "$input" == "q" ]]; then exit 1; fi
if [[ "$input" =~ ^[0-9]+$ ]]; then
PAGE="$input"
break
elif [[ "$input" == "n" ]]; then
if (( PAGE < GRID_NUM_PAGES )); then PAGE=$(( PAGE + 1 )); fi
elif [[ "$input" == "p" ]]; then
if (( PAGE > 1 )); then PAGE=$(( PAGE - 1 )); fi
fi
done
keyboard="${KB_LAYOUT[$((PAGE - 1))]}"
[[ $(tty 2>/dev/null) == "/dev/tty"* ]] && loadkeys "$keyboard" 2>/dev/null || true
# =========================
# 9. BASE INSTALL
# =========================
pacstrap -K /mnt \
base linux linux-firmware \
sudo networkmanager iwd
# --- 2. USER ---
while true; do
clear; read -rp "Username: " username
[[ "$username" =~ ^[a-z_][a-z0-9_-]*[$]?$ ]] && break
done
while true; do
clear; read -rsp "Password: " password; echo
read -rsp "Confirm: " password_confirmation; echo
[[ -n "$password" && "$password" == "$password_confirmation" ]] && break
done
password_hash=$(printf '%s' "$password" | openssl passwd -6 -stdin)
clear; read -rp "Hostname [archlinux]: " hostname
[[ -z "$hostname" ]] && hostname="archlinux"
genfstab -U /mnt >> /mnt/etc/fstab
# --- 3. TIMEZONE ---
clear; echo "Loading timezones..."
mapfile -t TIMEZONES < <(timedatectl list-timezones)
PAGE=1
COLS=100
ROWS=20
# =========================
# 10. CHROOT
# =========================
arch-chroot /mnt /bin/bash <<EOF
grid_menu TIMEZONES "$PAGE" "$COLS" "$ROWS"
set -e
while true; do
clear
echo "=== SELECT TIMEZONE ===" # TITRE
print_grid_menu $(( PAGE - 1 ))
echo -e "\n[n] Next | [p] Prev | [q] Quit"
read -rp "Entrée (n/p ou nombre) : " input
ln -sf "/usr/share/zoneinfo/$TIMEZONE" /etc/localtime
hwclock --systohc
if [[ "$input" == "q" ]]; then exit 1; fi
if [[ "$input" =~ ^[0-9]+$ ]]; then
PAGE="$input"
break
elif [[ "$input" == "n" ]]; then
if (( PAGE < GRID_NUM_PAGES )); then PAGE=$(( PAGE + 1 )); fi
elif [[ "$input" == "p" ]]; then
if (( PAGE > 1 )); then PAGE=$(( PAGE - 1 )); fi
fi
done
timezone="${TIMEZONES[$((PAGE - 1))]}"
echo "KEYMAP=$KEYMAP" > /etc/vconsole.conf
# --- 4. DISK ---
clear
local boot_source exclude_disk
boot_source=$(findmnt -no SOURCE /run/archiso/bootmnt 2>/dev/null || true)
local device_b
device_b=$(readlink -f "$boot_source" 2>/dev/null || echo "")
while [[ -n "$device_b" ]]; do
local parent_b
parent_b=$(lsblk -dno PKNAME "$device_b" 2>/dev/null | tail -n1)
[[ -z "$parent_b" ]] && break; device_b="/dev/$parent_b"
done
[[ $(lsblk -dno TYPE "$device_b" 2>/dev/null) == "disk" ]] && exclude_disk="$device_b"
echo "$LOCALE UTF-8" >> /etc/locale.gen
locale-gen
echo "LANG=$LOCALE" > /etc/locale.conf
DISKS=()
mapfile -t available_disks < <(lsblk -dpno NAME,TYPE | awk '$2=="disk"{print $1}' | grep -E '/dev/(sd|hd|vd|nvme|mmcblk|xv)' | { [[ -n "${exclude_disk:-}" ]] && grep -Fvx "$exclude_disk" || cat; })
for dev in "${available_disks[@]}"; do
local size; size=$(lsblk -dno SIZE "$dev" 2>/dev/null) || true
local model; model=$(lsblk -dno MODEL "$dev" 2>/dev/null | sed 's/ *$//') || true
DISKS+=("$dev ($size) - $model")
done
echo "$HOSTNAME" > /etc/hostname
PAGE=1
COLS=100
ROWS=10
useradd -m -G wheel -s /bin/bash "$USERNAME"
echo "$USERNAME:$USERPASS" | chpasswd
grid_menu DISKS "$PAGE" "$COLS" "$ROWS"
echo "%wheel ALL=(ALL:ALL) ALL" > /etc/sudoers.d/wheel
while true; do
clear
echo "=== SELECT INSTALLATION DISK ===" # TITRE
print_grid_menu $(( PAGE - 1 ))
echo -e "\n[n] Next | [p] Prev | [q] Quit"
read -rp "Entrée (n/p ou nombre) : " input
systemctl enable NetworkManager
if [[ "$input" == "q" ]]; then exit 1; fi
if [[ "$input" =~ ^[0-9]+$ ]]; then
PAGE="$input"
break
elif [[ "$input" == "n" ]]; then
if (( PAGE < GRID_NUM_PAGES )); then PAGE=$(( PAGE + 1 )); fi
elif [[ "$input" == "p" ]]; then
if (( PAGE > 1 )); then PAGE=$(( PAGE - 1 )); fi
fi
done
disk=$(echo "${DISKS[$((PAGE - 1))]}" | awk '{print $1}')
EOF
# --- 5. ENCRYPTION ---
clear; local encrypt_installation="false"
ask_confirm "Encrypt disk?" && encrypt_installation="true"
echo "========================"
echo " Installation terminée"
echo "========================"
reboot
# --- JSON GENERATION (100% BASH PUR, PLUS DE JQ) ---
# On échappe les variables et on les entoure de guillemets
local pw_esc="\"$(json_escape "$password")\""
local hash_esc="\"$(json_escape "$password_hash")\""
local user_esc="\"$(json_escape "$username")\""
local host_esc="\"$(json_escape "$hostname")\""
local tz_esc="\"$(json_escape "$timezone")\""
local kb_esc="\"$(json_escape "$keyboard")\""
local disk_esc="\"$(json_escape "$disk")\""
# Fichier identifiants
if [[ $encrypt_installation == "true" ]]; then
credentials_encryption_line=" \"encryption_password\": $pw_esc,"
else
credentials_encryption_line=""
fi
cat <<_EOF_ >user_credentials.json
{
$credentials_encryption_line
"root_enc_password": $hash_esc,
"users": [
{
"enc_password": $hash_esc,
"groups": [],
"sudo": true,
"username": $user_esc
}
]
}
_EOF_
# Calculs partitions
local disk_size; disk_size=$(lsblk -bdno SIZE "$disk" 2>/dev/null) || true
local mib=$((1024*1024))
local gib=$((mib*1024))
local disk_size_in_mib=$((disk_size / mib * mib))
local boot_size=$((2 * gib))
local main_start=$((boot_size + mib))
local main_size=$((disk_size_in_mib - main_start - mib))
# Fichier configuration principal
if [[ $encrypt_installation == true ]]; then
disk_encryption_config=$(cat <<_EOF_
,
"disk_encryption": {
"encryption_type": "luks",
"lvm_volumes": [],
"iter_time": 2000,
"partitions": [ "8c2c2b92-1070-455d-b76a-56263bab24aa" ],
"encryption_password": $pw_esc
}
_EOF_
)
else
disk_encryption_config=""
fi
cat <<_EOF_ >user_configuration.json
{
"archinstall-language": "English",
"audio_config": { "audio": "pipewire" },
"bootloader": "grub",
"disk_config": {
"btrfs_options": { "snapshot_config": { "type": "Snapper" } },
"config_type": "default_layout",
"device_modifications": [
{
"device": $disk_esc,
"partitions": [
{
"btrfs": [],
"dev_path": null,
"flags": [ "boot", "esp" ],
"fs_type": "fat32",
"mount_options": [],
"mountpoint": "/boot",
"obj_id": "ea21d3f2-82bb-49cc-ab5d-6f81ae94e18d",
"size": {
"sector_size": { "unit": "B", "value": 512 },
"unit": "B",
"value": $boot_size
},
"start": {
"sector_size": { "unit": "B", "value": 512 },
"unit": "B",
"value": $mib
},
"status": "create",
"type": "primary"
},
{
"btrfs": [
{ "mountpoint": "/", "name": "@" },
{ "mountpoint": "/home", "name": "@home" },
{ "mountpoint": "/var/log", "name": "@log" },
{ "mountpoint": "/var/cache/pacman/pkg", "name": "@pkg" }
],
"dev_path": null,
"flags": [],
"fs_type": "btrfs",
"mount_options": [ "compress=zstd" ],
"mountpoint": null,
"obj_id": "8c2c2b92-1070-455d-b76a-56263bab24aa",
"size": {
"sector_size": { "unit": "B", "value": 512 },
"unit": "B",
"value": $main_size
},
"start": {
"sector_size": { "unit": "B", "value": 512 },
"unit": "B",
"value": $main_start
},
"status": "create",
"type": "primary"
}
],
"wipe": true
}
]$disk_encryption_config
},
"hostname": $host_esc,
"kernels": [ "linux" ],
"network_config": { "type": "iso" },
"ntp": true,
"parallel_downloads": 8,
"swap": true,
"timezone": $tz_esc,
"locale_config": {
"kb_layout": $kb_esc,
"sys_enc": "UTF-8",
"sys_lang": "en_US.UTF-8"
},
"mirror_config": {
"custom_servers": [
{"url": "https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"},
{"url": "https://mirror.rackspace.com/archlinux/\$repo/os/\$arch"}
]
},
"packages": [
"base-devel",
"git",
"snapper"
],
"profile_config": {
"gfx_driver": null,
"greeter": null,
"profile": {}
},
"version": "3.0.9"
}
_EOF_
}
# ==========================================
# MAIN
# ==========================================
if [[ $(tty) == "/dev/tty1" ]]; then
run_configurator
clear
echo "Installing base system..."
install_base_system
echo "Base installation complete. You can now reboot or chroot into /mnt."
fi