Skip to content

Instantly share code, notes, and snippets.

@unixabg
Created February 20, 2026 14:37
Show Gist options
  • Select an option

  • Save unixabg/5d7f8c1e02f5da3efbe4712618334520 to your computer and use it in GitHub Desktop.

Select an option

Save unixabg/5d7f8c1e02f5da3efbe4712618334520 to your computer and use it in GitHub Desktop.
VelvetOS Install helpers
#!/bin/bash
#set -x
set -Eeuo pipefail
_VER='20260208.0-beta'
clear
echo "Script chromebook_x86_uefi-x86_64-trixie.sh installer version: $_VER"
MNT="/tmp/p3"
_cleaned_up=0
cleanup() {
# prevent double cleanup if multiple traps fire
if [[ "${_cleaned_up}" -eq 1 ]]; then return 0; fi
_cleaned_up=1
echo "Cleaning up mounts..."
# If anything is still running inside the chroot and holding mounts busy, try to kill it.
# (Safe even if not mounted / no processes)
if command -v fuser >/dev/null 2>&1; then
fuser -km "$MNT" 2>/dev/null || true
fi
# Unmount in reverse order: proc/sys/dev first, then the root mount
for mp in "$MNT/proc" "$MNT/sys" "$MNT/dev" "$MNT"; do
if mountpoint -q "$mp"; then
echo "Unmounting $mp"
umount "$mp" 2>/dev/null || umount -l "$mp" 2>/dev/null || true
fi
done
# Remove mount dir if not mounted anymore
if [[ -d "$MNT" ]] && ! mountpoint -q "$MNT"; then
rmdir "$MNT" 2>/dev/null || true
fi
}
# Always try to clean up on exit; also stop on signals/errors (EXIT will still run cleanup)
trap cleanup EXIT
trap 'echo "Interrupted."; exit 1' INT TERM
trap 'echo "Error on line $LINENO: $BASH_COMMAND"; exit 1' ERR
# Clean up leftovers from any previous failed run *before* starting fresh
cleanup
# allow cleanup again later
_cleaned_up=0
# Check if TGTDEV is passed as an argument
if [ -z "${1:-}" ]; then
echo "Usage: $0 <target_device>"
echo "Please provide the target device (e.g., sda or mmcblk0)."
exit 1
fi
export TGTDEV="$1"
# Confirm the target device
echo "WARNING: You are about to overwrite /dev/$TGTDEV. Make sure this is correct."
lsblk -p -o name,size,label | grep -w "/dev/$TGTDEV" || true
read -p "Do you want to proceed with this target device? (yes/no) " confirm_target
if [ "$confirm_target" != "yes" ]; then
echo "Exiting... Please double-check your target device."
exit 1
fi
# Determine partition prefix (e.g., /dev/sda3 vs /dev/mmcblk0p3)
if [[ "$TGTDEV" == mmcblk* ]]; then
PART_PREFIX="p"
else
PART_PREFIX=""
fi
PART3="/dev/${TGTDEV}${PART_PREFIX}3"
# dd the image to the target
echo "Summary:"
echo " Image to install: chromebook_x86_uefi-x86_64-trixie.img"
echo " Target device: /dev/$TGTDEV"
echo " Partition prefix: $PART_PREFIX"
echo "This will overwrite all data on /_attach to /dev/$TGTDEV."
read -p "Do you want to proceed? (yes/no) " yn
case $yn in
yes )
echo "Running dd to install image to emmc..."
dd if=chromebook_x86_uefi-x86_64-trixie.img of="/dev/$TGTDEV" bs=1024k status=progress
sync
echo "...completed attempt to install image to emmc!"
;;
no )
echo "Exiting..."
exit 0
;;
* )
echo "Invalid response"
exit 1
;;
esac
echo "Doing some tasks on the btrfs file system on $TGTDEV"
mkdir -p "$MNT"
mount "$PART3" "$MNT"
echo "Resizing to max the emmc root partition on $TGTDEV"
bash extend-rootfs-installer.sh
# Mount necessary filesystems for chroot environment
# Use rbind so nested mounts under /dev etc. are handled more robustly
mount --rbind /dev "$MNT/dev"
mount --rbind /sys "$MNT/sys"
mount --rbind /proc "$MNT/proc"
# Reduce mount propagation surprises (best effort; ignore if unsupported)
mount --make-rslave "$MNT/dev" 2>/dev/null || true
mount --make-rslave "$MNT/sys" 2>/dev/null || true
mount --make-rslave "$MNT/proc" 2>/dev/null || true
# Function to run a script in chroot and handle errors
run_in_chroot() {
local script_path="$1"
shift
local script_name
script_name="$(basename "$script_path")"
echo "Copying $script_path to chroot environment..."
cp "$script_path" "$MNT/$script_name"
echo "Running $script_name in chroot with arguments: $*"
chroot "$MNT" /bin/bash "/$script_name" "$@"
local status=$?
if [ $status -ne 0 ]; then
echo "Error: Script $script_name failed with exit code $status."
echo "Stopping for inspection. Fix the issue, then continue manually."
exit 1
else
echo "$script_name completed successfully."
fi
echo "Deleting $script_name from chroot environment..."
rm -f "$MNT/$script_name" || echo "Warning: Failed to delete $script_name from chroot environment."
}
# Run scripts one by one in chroot environment
#run_in_chroot "./kiosk-install.sh"
#run_in_chroot "./setup-dosi-phonehome" "kiosk"
run_in_chroot "./google-chrome-install.sh"
run_in_chroot "./macify-user.sh" "linux"
run_in_chroot "./final-things.sh"
# No manual unmount section needed anymore — cleanup() (trap EXIT) handles it.
# But we still do sync + partprobe before exiting, while mounts are still present.
sync
partprobe || { echo "Warning: partprobe failed"; }
echo "Here is an output for sanity check"
lsblk -o name,size,label
read -p "Do you want to reboot now? (yes/no) " reboot_ans
if [ "$reboot_ans" == "yes" ]; then
echo "Rebooting now..."
reboot
else
echo "Ready for any extra changes you may want. Enjoy Linux on your emmc of $TGTDEV"
fi
#!/bin/bash
echo ""
echo "resizing target /tmp/p3 uefi install root filesystem"
echo ""
ROOTDEVICE=$(mount | grep 'on /tmp/p3 type' | awk '{print $1}' | sed 's,^/dev/,,g')
ROOTFSTYPE=$(mount | grep 'on /tmp/p3 type' | awk '{print $5}')
ROOTPARTUUID=$(blkid | grep "/dev/$ROOTDEVICE" | awk '{print $5}' | sed 's,",,g' | awk -F= '{print $2}')
ROOTDEVICEBASE=$(echo $ROOTDEVICE | grep "mmcblk" | sed 's,p.*,,g')
ROOTDEVICEPARTNUMBER=$(echo $ROOTDEVICE | sed 's,mmcblk.p,,g')
# the above is for the mmcblkx case - below is for the sdx case
if [ "$ROOTDEVICEBASE" = "" ]; then
ROOTDEVICEBASE=$(echo $ROOTDEVICE | grep "sd" | head -c 3)
ROOTDEVICEPARTNUMBER=$(echo $ROOTDEVICE | sed 's,sd.,,g')
fi
# in case we cannot determine the root device base
if [ "$ROOTDEVICEBASE" = "" ]; then
echo ""
echo "cannot determine the root device base - giving up"
echo ""
exit 1
fi
PARTITIONTYPE=$(fdisk -l /dev/${ROOTDEVICEBASE} | grep "Disklabel type" | awk -F: '{print $2}' | sed 's,^ ,,g')
if [ "$PARTITIONTYPE" = "dos" ]; then
ROOTPARTITIONSTART=$(sfdisk -d /dev/$ROOTDEVICEBASE | grep $ROOTDEVICE | awk '{print $4}' | sed 's/,//g')
fdisk /dev/$ROOTDEVICEBASE << EOF
d
$ROOTDEVICEPARTNUMBER
n
p
$ROOTDEVICEPARTNUMBER
$ROOTPARTITIONSTART
p
w
EOF
elif [ "$PARTITIONTYPE" = "gpt" ]; then
ROOTPARTITIONSTART=$(sfdisk -d /dev/$ROOTDEVICEBASE | grep $ROOTDEVICE | awk '{print $4}' | sed 's/,//g')
fdisk /dev/$ROOTDEVICEBASE << EOF
d
$ROOTDEVICEPARTNUMBER
n
$ROOTDEVICEPARTNUMBER
$ROOTPARTITIONSTART
x
u
$ROOTDEVICEPARTNUMBER
$ROOTPARTUUID
r
p
w
EOF
fi
partprobe /dev/$ROOTDEVICE
if [ "$ROOTFSTYPE" = "btrfs" ]; then
btrfs filesystem resize max /tmp/p3
else
resize2fs /dev/$ROOTDEVICE
fi
#!/bin/bash
set -e
echo "Setting system timezone to US Central (America/Chicago)..."
ln -sf /usr/share/zoneinfo/America/Chicago /etc/localtime
echo "America/Chicago" > /etc/timezone
date
echo "Dropping /etc/apt/sources.list.d/velvet_repo.sources..."
rm -f /etc/apt/sources.list.d/velvet_repo.sources
#!/usr/bin/env bash
set -e
echo "Updating package list..."
apt update
echo "Installing required dependencies..."
apt install -y wget gnupg ca-certificates
echo "Adding Google Chrome GPG key..."
wget -qO- https://dl.google.com/linux/linux_signing_key.pub | \
gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg
echo "Adding Google Chrome repository..."
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | \
tee /etc/apt/sources.list.d/google-chrome.list > /dev/null
echo "Updating package list again..."
apt update
echo "Installing Google Chrome..."
apt install -y google-chrome-stable
echo "Installation complete!"
echo "You can launch Chrome with: google-chrome"
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
echo "Usage: sudo $0 <targetUserID>"
exit 1
fi
TARGET_USER="$1"
TS="$(date +%Y%m%d-%H%M%S)"
# ===== Desired look =====
GTK_THEME_NAME="${GTK_THEME_NAME:-Adwaita-dark}"
# IMPORTANT: icon theme folder name must match what's in /usr/share/icons
ICON_THEME_NAME="${ICON_THEME_NAME:-WhiteSur-dark}"
# Cursor: keep Adwaita by default (safe), or set to WhiteSur if you have it installed
CURSOR_THEME_NAME="${CURSOR_THEME_NAME:-Adwaita}"
# Window manager: keep user's current xfwm theme unless you want to force one.
# If you DO want a WhiteSur xfwm theme, set XFWM_THEME_NAME accordingly.
XFWM_THEME_NAME="${XFWM_THEME_NAME:-}"
FONT_NAME="${FONT_NAME:-Inter 10}"
MONO_FONT_NAME="${MONO_FONT_NAME:-Fira Code 10}"
WORKDIR="${WORKDIR:-/opt/mactheme-build}"
# =======================
require_root() { [[ "${EUID}" -eq 0 ]] || { echo "ERROR: run with sudo/root"; exit 1; }; }
user_exists() { id "$TARGET_USER" >/dev/null 2>&1; }
user_home() { getent passwd "$TARGET_USER" | cut -d: -f6; }
backup_path_if_exists() {
local path="$1"
local backup_dir="$2"
if [[ -e "$path" ]]; then
mkdir -p "$backup_dir"
cp -a "$path" "$backup_dir/" || true
fi
}
apt_install() {
echo "[*] Installing required packages (best effort)..."
apt-get update
apt-get install -y --no-install-recommends \
git ca-certificates \
fonts-inter fonts-noto fonts-noto-color-emoji fonts-firacode \
gtk2-engines-murrine gtk2-engines-pixbuf \
xfce4-settings \
|| true
}
clone_or_update() {
local url="$1"
local dir="$2"
if [[ -d "${dir}/.git" ]]; then
git -C "$dir" fetch --depth=1 origin || true
git -C "$dir" reset --hard origin/master 2>/dev/null || git -C "$dir" reset --hard origin/main
else
git clone --depth=1 "$url" "$dir"
fi
}
install_whitesur_icons() {
# You asked for WhiteSur icons (dark). We'll install the icon theme repo system-wide.
echo "[*] Installing WhiteSur icon theme system-wide..."
mkdir -p "$WORKDIR"
local dir="${WORKDIR}/WhiteSur-icon-theme"
clone_or_update "https://github.com/vinceliuice/WhiteSur-icon-theme.git" "$dir"
# Install into /usr/share/icons. Keep name default "WhiteSur" unless user sets -n.
# The repo supports -t (variant). We'll install all variants so "WhiteSur-dark" can exist if provided by theme.
( cd "$dir" && bash ./install.sh -d /usr/share/icons -t all ) || true
}
# ---------- Apply appearance WITHOUT touching panel ----------
write_xsettings_xml() {
cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xsettings" version="1.0">
<property name="Net" type="empty">
<property name="ThemeName" type="string" value="${GTK_THEME_NAME}"/>
<property name="IconThemeName" type="string" value="${ICON_THEME_NAME}"/>
</property>
<property name="Gtk" type="empty">
<property name="CursorThemeName" type="string" value="${CURSOR_THEME_NAME}"/>
<property name="FontName" type="string" value="${FONT_NAME}"/>
<property name="MonospaceFontName" type="string" value="${MONO_FONT_NAME}"/>
<property name="MenuImages" type="bool" value="true"/>
<property name="ButtonImages" type="bool" value="true"/>
</property>
</channel>
EOF
}
write_xfwm4_xml_preserve_theme() {
# Only enforce mac-style button layout + title font.
# Preserve user's existing xfwm theme unless XFWM_THEME_NAME is set.
local theme_line=""
if [[ -n "${XFWM_THEME_NAME}" ]]; then
theme_line=" <property name=\"theme\" type=\"string\" value=\"${XFWM_THEME_NAME}\"/>"
fi
cat <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfwm4" version="1.0">
<property name="general" type="empty">
${theme_line}
<property name="button_layout" type="string" value="CHM|O"/>
<property name="title_font" type="string" value="${FONT_NAME}"/>
</property>
</channel>
EOF
}
apply_appearance_preserve_panel() {
local home="$1"
local base="${home}/.config/xfce4/xfconf/xfce-perchannel-xml"
mkdir -p "$base"
# Do NOT write xfce4-panel.xml, do NOT touch ~/.config/xfce4/panel
write_xsettings_xml > "${base}/xsettings.xml"
write_xfwm4_xml_preserve_theme > "${base}/xfwm4.xml"
}
remove_plank_for_user() {
# In case previous attempts enabled it, remove user plank config and autostart entry.
local home="$1"
rm -rf "${home}/.config/plank" || true
# Remove autostart launchers for plank if present
if [[ -d "${home}/.config/autostart" ]]; then
rm -f "${home}/.config/autostart/plank.desktop" || true
fi
# Also remove system-wide plank autostart file if you had created one earlier
# (safe to ignore if it doesn't exist)
rm -f /etc/xdg/autostart/plank.desktop 2>/dev/null || true
}
verify_icon_theme_exists() {
# If the user requested ICON_THEME_NAME but it's not installed, fall back to "WhiteSur" or "Adwaita".
if [[ -d "/usr/share/icons/${ICON_THEME_NAME}" ]]; then
return 0
fi
# Common fallback for this icon theme repo is "WhiteSur"
if [[ -d "/usr/share/icons/WhiteSur" ]]; then
echo "[!] Icon theme '${ICON_THEME_NAME}' not found; falling back to 'WhiteSur'."
ICON_THEME_NAME="WhiteSur"
return 0
fi
echo "[!] Icon theme '${ICON_THEME_NAME}' not found; falling back to 'Adwaita'."
ICON_THEME_NAME="Adwaita"
}
main() {
require_root
user_exists || { echo "ERROR: user '$TARGET_USER' not found"; exit 1; }
local home
home="$(user_home)"
[[ -n "$home" && -d "$home" ]] || { echo "ERROR: home for '$TARGET_USER' not found"; exit 1; }
apt_install
install_whitesur_icons
verify_icon_theme_exists
# Backup what we might touch (and panel too, just in case)
local backup="${home}/desktop-macify-backup-${TS}"
echo "[*] Backing up to: $backup"
mkdir -p "$backup"
chown "$TARGET_USER:$TARGET_USER" "$backup" || true
backup_path_if_exists "${home}/.config/xfce4" "$backup"
backup_path_if_exists "${home}/.config/autostart" "$backup"
backup_path_if_exists "${home}/.config/plank" "$backup"
echo "[*] Preserving top bar functionality (no panel changes), applying new look..."
apply_appearance_preserve_panel "$home"
echo "[*] Removing Plank settings/autostart for the user..."
remove_plank_for_user "$home"
echo "[*] Fixing ownership..."
chown -R "$TARGET_USER:$TARGET_USER" "${home}/.config" "${home}/.cache" || true
echo
echo "============================================================"
echo "Done for '${TARGET_USER}'."
echo "Top bar/panel layout untouched."
echo "GTK theme: ${GTK_THEME_NAME}"
echo "Icon theme: ${ICON_THEME_NAME}"
echo "Backup: ${backup}"
echo "Next: log out and log back in to reload settings cleanly."
echo "============================================================"
}
main
#!/bin/bash
set -e
echo "Setting system timezone to US Central (America/Chicago)..."
timedatectl set-timezone America/Chicago
echo "Timezone now set to:"
timedatectl | grep "Time zone"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment