309 lines
8.0 KiB
Bash
309 lines
8.0 KiB
Bash
#!/bin/bash
|
|
#
|
|
# hyperbola-bootstrap: Bootstrap a base Hyperbola GNU+Linux-libre system using any GNU distribution.
|
|
#
|
|
# Project: https://git.sr.ht/~heckyel/hyperbola-bootstrap
|
|
#
|
|
# Usage:
|
|
#
|
|
# # ./hyper-bootstrap.sh destination
|
|
# # ./hyper-bootstrap.sh -a x86_64 -r https://repo.hyperbola.info:50011/gnu-plus-linux-libre/testing destination-64
|
|
#
|
|
# Example:
|
|
#
|
|
# # ./hyper-bootstrap.sh -a x86_64 -r "https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/testing" myhyper
|
|
# # ./hyper-bootstrap.sh myhyper
|
|
#
|
|
|
|
set -e -u -o pipefail
|
|
|
|
# Packages needed by pacman (see get-pacman-dependencies.sh)
|
|
PACMAN_PACKAGES=(
|
|
acl
|
|
attr
|
|
bash
|
|
bzip2
|
|
ca-certificates
|
|
ca-certificates-mozilla
|
|
coreutils
|
|
curl
|
|
e2fsprogs
|
|
expat
|
|
findutils
|
|
gcc-libs
|
|
gettext-tiny
|
|
glibc
|
|
gmp
|
|
gnupg-stable
|
|
gnutls
|
|
gpgme
|
|
grep
|
|
gzip
|
|
hyperbola-keyring
|
|
icu
|
|
keyutils
|
|
krb5
|
|
libarchive
|
|
libassuan
|
|
libcap
|
|
libffi
|
|
libldap
|
|
libgcrypt
|
|
libgpg-error
|
|
libidn2
|
|
libksba
|
|
libnghttp2
|
|
libpsl
|
|
libressl
|
|
libsasl
|
|
libssh2
|
|
libtasn1
|
|
libunistring
|
|
lsb-release
|
|
lz4
|
|
lzip
|
|
lzo
|
|
ncurses
|
|
nettle
|
|
npth
|
|
p11-kit
|
|
pacman
|
|
pacman-mirrorlist
|
|
pcre
|
|
pinentry
|
|
pinentry-tty
|
|
readline
|
|
sed
|
|
shadow
|
|
sqlite
|
|
xz
|
|
zlib
|
|
)
|
|
BASIC_PACKAGES=(${PACMAN_PACKAGES[*]} filesystem)
|
|
EXTRA_PACKAGES=(gawk file tar hyperrc)
|
|
DEFAULT_REPO_URL="https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/testing"
|
|
|
|
stderr() {
|
|
echo "$@" >&2
|
|
}
|
|
|
|
debug() {
|
|
echo -e "\e[1;32m==>\e[0m\033[1m $* \e[m"
|
|
}
|
|
|
|
extract_href() {
|
|
sed -n '/<a / s/^.*<a [^>]*href="\([^\"]*\)".*$/\1/p'
|
|
}
|
|
|
|
fetch() {
|
|
curl -L -sS --retry 5 --connect-timeout 10 --max-time 30 "$@"
|
|
}
|
|
|
|
fetch_file() {
|
|
local FILEPATH=$1
|
|
shift
|
|
if [[ -e "$FILEPATH" ]]; then
|
|
curl -L --retry 5 --connect-timeout 10 --max-time 30 -z "$FILEPATH" -o "$FILEPATH" "$@"
|
|
else
|
|
curl -L --retry 5 --connect-timeout 10 --max-time 30 -o "$FILEPATH" "$@"
|
|
fi
|
|
}
|
|
|
|
uncompress() {
|
|
local FILEPATH=$1 DEST=$2
|
|
|
|
case "$FILEPATH" in
|
|
*.gz)
|
|
tar xzf "$FILEPATH" -C "$DEST";;
|
|
*.xz)
|
|
xz -dc "$FILEPATH" | tar x -C "$DEST";;
|
|
*.lz)
|
|
tar xf "$FILEPATH" -C "$DEST";;
|
|
*)
|
|
debug "Error: unknown package format: $FILEPATH"
|
|
return 1;;
|
|
esac
|
|
}
|
|
|
|
get_default_repo() {
|
|
local ARCH=$1
|
|
if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
|
|
echo $DEFAULT_REPO_URL
|
|
fi
|
|
}
|
|
|
|
get_core_repo_url() {
|
|
local REPO_URL=$1 ARCH=$2
|
|
if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
|
|
echo "${REPO_URL%/}/core/os/$ARCH"
|
|
fi
|
|
}
|
|
|
|
get_template_repo_url() {
|
|
local REPO_URL=$1 ARCH=$2
|
|
if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
|
|
echo "${REPO_URL%/}/\$repo/os/$ARCH"
|
|
fi
|
|
}
|
|
|
|
configure_pacman() {
|
|
local DEST=$1 ARCH=$2
|
|
debug "Configuring SERVER"
|
|
SERVER=$(get_template_repo_url "$REPO_URL" "$ARCH")
|
|
echo "Server = $SERVER" > "$DEST/etc/pacman.d/mirrorlist"
|
|
sed -i "s|^Architecture = auto|Architecture = $ARCH|" "$DEST/etc/pacman.conf"
|
|
debug "Configuring CERT"
|
|
cp -fv certs/1.pem "$DEST/etc/ca-certificates/extracted/tls-ca-bundle.pem"
|
|
}
|
|
|
|
clean_chroot() {
|
|
local DEST=$1
|
|
debug "Clean Chroot"
|
|
rm -rf "$DEST/.BUILDINFO" "$DEST/.INSTALL" "$DEST/.MTREE" "$DEST/.PKGINFO" || true
|
|
for i in $DEST/usr/share/{doc,man,info}; do
|
|
rm -rf "$i" && install -d "$i"
|
|
done
|
|
truncate -s 0 "$DEST/var/log/pacman.log"
|
|
}
|
|
|
|
configure_minimal_system() {
|
|
local DEST=$1
|
|
|
|
mkdir -p "$DEST/dev"
|
|
sed -ie 's|^root:.*$|root:$1$GT9AUpJe$oXANVIjIzcnmOpY07iaGi/:14657::::::|' "$DEST/etc/shadow"
|
|
touch "$DEST/etc/group"
|
|
echo "nameserver 9.9.9.9" > "$DEST/etc/resolv.conf"
|
|
echo "bootstrap" > "$DEST/etc/hostname"
|
|
|
|
rm -f "$DEST/etc/mtab"
|
|
echo "rootfs / rootfs rw 0 0" > "$DEST/etc/mtab"
|
|
test -e "$DEST/dev/null" || mknod "$DEST/dev/null" c 1 3
|
|
test -e "$DEST/dev/random" || mknod -m 0644 "$DEST/dev/random" c 1 8
|
|
test -e "$DEST/dev/urandom" || mknod -m 0644 "$DEST/dev/urandom" c 1 9
|
|
|
|
sed -i "s|^[[:space:]]*\(CheckSpace\)|# \1|" "$DEST/etc/pacman.conf"
|
|
}
|
|
|
|
set_final_conf() {
|
|
local DEST=$1 ARCH=$2
|
|
debug 'Rename files ending in .pacnew...'
|
|
find "${DEST}" -type f -name "*.pacnew" -exec sh -c 'mv -f "$1" "${1%.pacnew}"' _ {} \;
|
|
debug 'Update pacman.conf...'
|
|
sed -i "s|SigLevel = Never|SigLevel = Required DatabaseOptional|" "$DEST/etc/pacman.conf"
|
|
sed -i "s|^Architecture = auto|Architecture = $ARCH|" "$DEST/etc/pacman.conf"
|
|
}
|
|
|
|
configure_locale() {
|
|
local DEST=$1
|
|
sed -e 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' -i "$DEST/etc/locale.gen"
|
|
echo LANG=en_US.UTF-8 > "$DEST/etc/locale.conf"
|
|
LC_ALL=C chroot "$DEST" \
|
|
locale-gen
|
|
}
|
|
|
|
fetch_packages_list() {
|
|
local REPO=$1
|
|
|
|
debug "Fetch packages list: $REPO/"
|
|
fetch "$REPO/" | extract_href | awk -F"/" '{print $NF}' | sort -rn ||
|
|
{ debug "Error: cannot fetch packages list: $REPO"; return 1; }
|
|
}
|
|
|
|
install_pacman_packages() {
|
|
local BASIC_PACKAGES=$1 DEST=$2 LIST=$3 DOWNLOAD_DIR=$4
|
|
debug "pacman package and dependencies: $BASIC_PACKAGES"
|
|
|
|
for PACKAGE in $BASIC_PACKAGES; do
|
|
local FILE=$(echo "$LIST" | grep -m1 "^$PACKAGE-[[:digit:]].*\(\.gz\|\.xz\|\.lz\|\.zst\)$")
|
|
test "$FILE" || { debug "Error: cannot find package: $PACKAGE"; return 1; }
|
|
local FILEPATH="$DOWNLOAD_DIR/$FILE"
|
|
|
|
debug "Download package: $REPO/$FILE"
|
|
fetch_file "$FILEPATH" "$REPO/$FILE"
|
|
debug "Uncompress package: $FILEPATH"
|
|
uncompress "$FILEPATH" "$DEST"
|
|
done
|
|
}
|
|
|
|
configure_static_qemu() {
|
|
local ARCH=$1 DEST=$2
|
|
[[ "$ARCH" == arm* ]] && ARCH=arm
|
|
QEMU_STATIC_BIN=$(command -v qemu-$ARCH-static || echo )
|
|
[[ -e "$QEMU_STATIC_BIN" ]] ||\
|
|
{ debug "No static qemu for $ARCH, ignoring"; return 0; }
|
|
cp "$QEMU_STATIC_BIN" "$DEST/usr/bin"
|
|
}
|
|
|
|
install_packages() {
|
|
local ARCH=$1 DEST=$2 PACKAGES=$3
|
|
debug "Install packages: $PACKAGES"
|
|
LC_ALL=C chroot "$DEST" \
|
|
/usr/bin/dirmngr </dev/null &>/dev/null
|
|
LC_ALL=C chroot "$DEST" \
|
|
/usr/bin/pacman-key --init
|
|
LC_ALL=C chroot "$DEST" \
|
|
/usr/bin/pacman-key --populate hyperbola
|
|
LC_ALL=C chroot "$DEST" \
|
|
/usr/bin/pacman --noconfirm --noprogressbar --quiet --arch $ARCH -Syy --force $PACKAGES
|
|
LC_ALL=C chroot "$DEST" \
|
|
/usr/bin/pacman --noconfirm --noprogressbar --quiet -Scc
|
|
}
|
|
|
|
show_usage() {
|
|
stderr "Usage: $(basename "$0") [-q] [-a i686|x86_64|arm] [-r REPO_URL] [-d DOWNLOAD_DIR] DESTDIR"
|
|
}
|
|
|
|
main() {
|
|
# Process arguments and options
|
|
test $# -eq 0 && set -- "-h"
|
|
local ARCH=
|
|
local REPO_URL=
|
|
local USE_QEMU=
|
|
local DOWNLOAD_DIR=
|
|
local PRESERVE_DOWNLOAD_DIR=
|
|
|
|
while getopts "qa:r:d:h" ARG; do
|
|
case "$ARG" in
|
|
a) ARCH=$OPTARG;;
|
|
r) REPO_URL=$OPTARG;;
|
|
q) USE_QEMU=true;;
|
|
d) DOWNLOAD_DIR=$OPTARG
|
|
PRESERVE_DOWNLOAD_DIR=true;;
|
|
*) show_usage; return 1;;
|
|
esac
|
|
done
|
|
shift $(($OPTIND-1))
|
|
test $# -eq 1 || { show_usage; return 1; }
|
|
|
|
[[ -z "$ARCH" ]] && ARCH=$(uname -m)
|
|
[[ -z "$REPO_URL" ]] && REPO_URL=$(get_default_repo "$ARCH")
|
|
|
|
local DEST=$1
|
|
local REPO=$(get_core_repo_url "$REPO_URL" "$ARCH")
|
|
[[ -z "$DOWNLOAD_DIR" ]] && DOWNLOAD_DIR=$(mktemp -d)
|
|
mkdir -p "$DOWNLOAD_DIR"
|
|
[[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && trap "rm -rf '$DOWNLOAD_DIR'" TERM EXIT
|
|
debug "Destination directory: $DEST"
|
|
debug "Core repository: $REPO"
|
|
debug "Temporary directory: $DOWNLOAD_DIR"
|
|
|
|
# Fetch packages, install system and do a minimal configuration
|
|
mkdir -p "$DEST"
|
|
local LIST=$(fetch_packages_list $REPO)
|
|
install_pacman_packages "${BASIC_PACKAGES[*]}" "$DEST" "$LIST" "$DOWNLOAD_DIR"
|
|
configure_pacman "$DEST" "$ARCH"
|
|
configure_minimal_system "$DEST"
|
|
[[ -n "$USE_QEMU" ]] && configure_static_qemu "$ARCH" "$DEST"
|
|
install_packages "$ARCH" "$DEST" "${BASIC_PACKAGES[*]} ${EXTRA_PACKAGES[*]}"
|
|
configure_locale "$DEST"
|
|
clean_chroot "$DEST" # clean
|
|
[[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && rm -rf "$DOWNLOAD_DIR"
|
|
set_final_conf "$DEST" "$ARCH"
|
|
debug "Done!"
|
|
debug
|
|
debug "You may now chroot or arch-chroot from package arch-install-scripts:"
|
|
debug "$ doas arch-chroot $DEST"
|
|
}
|
|
|
|
main "$@"
|