You are not logged in.

#1 2024-07-24 10:58:16

zogstrip
Member
Registered: 2024-07-24
Posts: 2
Website

Framework Laptop 13 (13th Gen Intel) install script

Hello

I recently acquired a Framework laptop and decided to mess around with Arch Linux to build a lightweight yet modern system.

I've been working on an automated install script to set everything up from scratch while learning how it all works.

Here are the laptop's specs

  • CPU Intel® Core™ i7-1360P

  • SSD WD_BLACK™ SN850X NVMe™- M.2 2280 - 1TB

  • RAM DDR4-3200 - 16GB (2 x 8GB)

Goals

  • Minimal

  • Modern

  • Keyboard-focused

  • Ruby/PHP dev environment

Here's what I've settled for

  • Display server Wayland

  • Compositor Sway

  • Terminal foot

  • Shell bash

  • Text editor vim

In order to install Arch, I first connect to my Wi-Fi and then `curl | sh` the install script using the following commands

iwctl station wlan0 connect <SSID>
curl -fsSL https://.../arch.sh | sh

Here's the latest version of the install script.

I'm still far away from having a complete system, but if I'm making any stupid mistakes, or forgetting something, I'd rather know now than when it's too late.

#!/bin/bash

set -euo pipefail
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR

step() { 
  echo -e "\n[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

SSD="nvme0n1"
PARTITION_EFI="/dev/${SSD}p1"
PARTITION_ROOT="/dev/${SSD}p2"
LUKS="luks"
TIMEZONE="Europe/Paris"
LOCALE="en_US.UTF-8"
HOSTNAME="framezork"
USER="zogstrip"
PACKAGES=(
  base
  base-devel # dev tools
  linux
  linux-firmware
  intel-ucode
  tlp # power management
  ethtool # disable Wake-on-LAN
  smartmontools # S.M.A.R.T
  util-linux # fstrim
  reflector # pacman's mirrorlist
  fprintd # fingerprint reader
  pipewire # audio
  pipewire-pulse # bluetooth audio
  mesa # video
  iwd # wifi
  bluez # bluetooth
  bluez-utils # bluetoothctl
  tailscale # vpn
  curl
  wget
  httpie
  man-db
  man-pages
  tldr # better man
  fastfetch # better neofetch
  git
  pass # password manager
  btop # better top
  vim # text editor
  fzf # fuzzy finder
  eza # better ls
  zoxide # better cd
  bat # better cat
  ripgrep # better grep
  jq # json
  sway # window manager
  foot # terminal emulator
  imagemagick # image manipulation
  ffmpeg # video manipulation
  mpv # video watching
  grim # wayland screen grabber
  firefox
  zeal # offline documentation
)

###
### 0 - checks
###

VENDOR=$(cat /sys/class/dmi/id/sys_vendor)
CPU=$(lscpu | awk -F: '/^Vendor ID:/ {print $2}' | xargs)
BATTERY_STATUS=$(cat /sys/class/power_supply/BAT*/status | head -n 1)
BATTERY_CAPACITY=$(cat /sys/class/power_supply/BAT*/capacity | head -n 1)

[[ -d "/sys/firmware/efi" ]] || { echo "System not booted in UEFI mode."; exit 1; }
[[ -d "/sys/block/${SSD}" ]] || { echo "NVMe drive not found."; exit 1; }
[[ "$VENDOR" == "Framework" ]] || { echo "Not running on a Framework laptop. Found $VENDOR."; exit 1; }
[[ "$CPU" == *"Intel"* ]] || { echo "Not running on Intel CPU. Found $CPU."; exit 1; }
[[ "$BATTERY_STATUS" != "Discharging" || "$BATTERY_CAPACITY" -ge 20 ]] || { echo "Battery too low."; exit 1; }

step "Installing dialog..."
pacman -Sy --noconfirm dialog

PASSWORD=$(dialog --clear --stdout --passwordbox "Enter password" 0 0)
clear

[[ -z "$PASSWORD" ]] && { echo "Empty password. Aborting."; exit 1; }

###
### 1 - pre-installation
###

step "Unmounting..."
umount -R /mnt 2> /dev/null || true
cryptsetup luksClose $LUKS 2> /dev/null || true

step "Setting up disks & partitions..."
sgdisk -Z /dev/$SSD
sgdisk -n 1::+512M -t 1:ef00 /dev/$SSD  # EFI System Partition
sgdisk -n 2::-0 -t 2:8300 /dev/$SSD  # Linux filesystem

step "Formatting EFI partition..."
mkfs.vfat -F32 $PARTITION_EFI

step "Formatting ROOT partition..."
echo -n "${PASSWORD}" | cryptsetup luksFormat $PARTITION_ROOT -
echo -n "${PASSWORD}" | cryptsetup luksOpen --allow-discards --persistent $PARTITION_ROOT $LUKS -
mkfs.ext4 /dev/mapper/$LUKS

step "Mounting partitions..."
mount /dev/mapper/$LUKS /mnt
mkdir /mnt/boot
mount $PARTITION_EFI /mnt/boot

###
### 2 - installation
###

step "Configuring initrd..."
mkdir /mnt/etc
cat <<EOF > /mnt/etc/mkinitcpio.conf
HOOKS=(systemd autodetect modconf kms keyboard block sd-encrypt filesystems fsck)
EOF

step "Setting up mirrors (~ 1 min)..."
systemctl start reflector

step "Installing packages..."
pacstrap /mnt "${PACKAGES[@]}"

###
### 3 - configuration
###

step "Generating fstab..."
genfstab -U /mnt >> /mnt/etc/fstab

step "Setting timezone..."
ln -sf /usr/share/zoneinfo/$TIMEZONE /mnt/etc/localtime
arch-chroot /mnt hwclock --systohc

step "Setting locale..."
echo "LANG=$LOCALE" > /mnt/etc/locale.conf
echo "$LOCALE UTF-8" > /mnt/etc/locale.gen
arch-chroot /mnt locale-gen

step "Setting hostname..."
echo "$HOSTNAME" > /mnt/etc/hostname
cat <<EOF >> /mnt/etc/hosts
127.0.0.1   localhost
::1         localhost
EOF

step "Copying WiFi credentials..."
mkdir -p /mnt/var/lib/iwd
cp /var/lib/iwd/*.psk /mnt/var/lib/iwd

step "Creating user $USER..."
arch-chroot /mnt useradd -m -G wheel $USER
echo "$USER:$PASSWORD" | arch-chroot /mnt chpasswd
echo "%wheel ALL=(ALL) ALL" > /mnt/etc/sudoers.d/wheel
chmod 440 /mnt/etc/sudoers.d/wheel

step "Disabling root..."
arch-chroot /mnt passwd -dl root

step "Installing bootloader..."
arch-chroot /mnt bootctl install

step "Configuring bootloader..."
UUID=$(blkid -s UUID -o value $PARTITION_ROOT)
cat <<EOF > /mnt/boot/loader/entries/arch.conf
title I use Arch btw
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options rd.luks.name=${UUID}=${LUKS} rd.luks.options=password-echo=no root=/dev/mapper/${LUKS} rw
EOF

cat <<EOF > /mnt/boot/loader/loader.conf
default arch
timeout 0
editor no
EOF

###
### 5 - setup
###

step "Configuring systemd..."
mkdir -p /mnt/etc/systemd/system.conf.d
cat <<EOF > /mnt/etc/systemd/system.conf.d/kill-fast.conf
[Manager]
DefaultTimeoutStopSec=3s
EOF

step "Setting up autologin on tty1..."
mkdir -p /mnt/etc/systemd/system/getty@tty1.service.d
cat <<EOF > /mnt/etc/systemd/system/getty@tty1.service.d/autologin.conf
[Service]
ExecStart=
ExecStart=-/usr/bin/agetty --autologin $USER --noclear %I \$TERM
EOF

step "Configuring networkd..."
cat <<EOF > /mnt/etc/systemd/network/20-wired.network
[Match]
Name=en*
[Network]
DHCP=yes
[DHCP]
RouteMetric=100
EOF

cat <<EOF > /mnt/etc/systemd/network/25-wireless.network
[Match]
Name=wl*
[Network]
DHCP=yes
[DHCP]
RouteMetric=600
IgnoreCarrierLoss=3s
EOF

step "Allowing wheel members to enroll fingerprints..."
cat <<EOF > /mnt/etc/polkit-1/rules.d/49-fprintd.rules
polkit.addRule(function(action, subject) {
    if (action.id == "net.reactivated.fprint.device.enroll" && subject.isInGroup("wheel")) {
        return polkit.Result.YES;
    }
});
EOF

step "Disabling power button..."
mkdir -p /mnt/etc/systemd/logind.conf.d
cat <<EOF > /mnt/etc/systemd/logind.conf.d/powerkey.conf
[Login]
HandlePowerKey=ignore
EOF

step "Remapping CAPS to ESC..."
mkdir -p /mnt/etc/udev/hwdb.d
cat <<EOF > /mnt/etc/udev/hwdb.d/90-caps-to-esc.hwdb
evdev:atkbd:*
 KEYBOARD_KEY_3a=esc
EOF

arch-chroot /mnt systemd-hwdb update

step "Configuring 30Hz key repeats..."
cat <<EOF > /mnt/etc/systemd/system/kbdrate.service
[Unit]
Description=Faster kbdrate (30Hz key repeat)

[Service]
ExecStart=/usr/bin/kbdrate -r 30
Type=oneshot
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

step "Forcing bluetooth's BR/EDR mode..."
sed -i '/^.*ControllerMode.*=.*/c\ControllerMode = bredr' /mnt/etc/bluetooth/main.conf

step "Enabling systemd timers..."
arch-chroot /mnt systemctl enable \
  fstrim.timer \
  reflector.timer

step "Enabling systemd services..."
arch-chroot /mnt systemctl enable \
  bluetooth.service \
  fprintd.service \
  iwd.service \
  kbdrate.service \
  reflector.service \
  systemd-networkd.service \
  systemd-resolved.service \
  systemd-timesyncd.service \
  tlp.service

###
### 6 - dotfiles
###

step "Setting up .bash_profile..."
cat <<EOF > /mnt/home/$USER/.bash_profile
# source .bashrc if it exists
[[ -f ~/.bashrc ]] && . ~/.bashrc

# launch sway on tty1
[[ -z \$DISPLAY && \$(tty) = /dev/tty1 ]] && exec sway
EOF

arch-chroot /mnt chown $USER:$USER /home/$USER/.bash_profile

###
### done
###

step "Unmounting..."
umount -R /mnt
cryptsetup luksClose $LUKS

step "Installation complete!"
step "You may 'reboot'."

Note: this was heavily inspired by maximbaz's install.sh.

Last edited by zogstrip (2024-07-24 11:32:42)

Offline

#2 2024-07-24 11:33:33

cryptearth
Member
Registered: 2024-02-03
Posts: 824

Re: Framework Laptop 13 (13th Gen Intel) install script

according to the guide en_US.UTF-8 is always required - your script will break when changing the master locale
also: I noticed that pacstrap base-devel xan lead to issues because locale.gen isn't configured yet - hence I would do a two pass: first install base and set up locale and second pass install devel

Offline

#3 2024-07-24 12:12:09

zogstrip
Member
Registered: 2024-07-24
Posts: 2
Website

Re: Framework Laptop 13 (13th Gen Intel) install script

cryptearth wrote:

according to the guide en_US.UTF-8 is always required - your script will break when changing the master locale

That's fine as I don't plan on using any other locale any time soon. Even though it's a variable, it's mostly to keep repeating myself and centralizing configuration.

cryptearth wrote:

also: I noticed that pacstrap base-devel can lead to issues because locale.gen isn't configured yet - hence I would do a two pass: first install base and set up locale and second pass install devel

That's interesting. What kind of issues? During the install or when using any of the programs/libs it contains?

Last edited by zogstrip (2024-07-24 12:12:55)

Offline

#4 2024-07-24 14:00:44

cryptearth
Member
Registered: 2024-02-03
Posts: 824

Re: Framework Laptop 13 (13th Gen Intel) install script

both:
during initial pacstrap you get several warnings about not set locale
and even after fixing the locale sometimes I got weird issues related to it which could be fixed by reinstalling base-devel after locale was set up

Offline

Board footer

Powered by FluxBB