hyper-bootstrap_v0.3.sh 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #!/bin/bash
  2. #
  3. # hyperbola-bootstrap: Bootstrap a base Hyperbola GNU+Linux-libre system using any GNU distribution.
  4. #
  5. # Dependencies: bash >= 4, coreutils, wget, sed, gawk, tar, gzip, chroot, xz, zstd.
  6. # Project: https://git.sr.ht/~heckyel/hyperbola-bootstrap
  7. #
  8. # Usage:
  9. #
  10. # # ./hyper-bootstrap.sh destination
  11. # # ./hyper-bootstrap.sh -a x86_64 -r https://repo.hyperbola.info:50012/gnu-plus-linux-libre/stable destination-64
  12. #
  13. # Example:
  14. #
  15. # # ./hyper-bootstrap.sh -a x86_64 -r "https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/stable" myhyper
  16. # # ./hyper-bootstrap.sh myhyper
  17. #
  18. # And then you can chroot to the destination directory (user: root, password: root):
  19. #
  20. # # chroot destination
  21. #
  22. # Note that some packages require some system directories to be mounted. Some of the commands you can try:
  23. #
  24. # # mount --bind /proc myhyper/proc
  25. # # mount --bind /sys myhyper/sys
  26. # # mount --bind /dev myhyper/dev
  27. # # mount --bind /dev/pts myhyper/dev/pts
  28. #
  29. set -e -u -o pipefail
  30. # Packages needed by pacman (see get-pacman-dependencies.sh)
  31. PACMAN_PACKAGES=(
  32. bash acl hyperbola-keyring attr bzip2 curl expat glibc gpgme libarchive grep sed coreutils
  33. libassuan libgpg-error libnghttp2 libssh2 lzo libressl pacman pacman-mirrorlist xz zlib libffi
  34. krb5 e2fsprogs keyutils libidn gcc-libs lz4 libpsl icu readline libunistring findutils
  35. ncurses pinentry lsb-release ca-certificates ca-certificates-utils p11-kit libtasn1
  36. libcap shadow pcre gzip
  37. )
  38. CORE_PACKAGES=(${PACMAN_PACKAGES[*]} filesystem)
  39. COMMUNITY_PACKAGES=(lzip zstd)
  40. EXTRA_PACKAGES=(gawk file tar openrc)
  41. DEFAULT_REPO_URL="https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/stable"
  42. stderr() {
  43. echo "$@" >&2
  44. }
  45. debug() {
  46. echo -e "\e[1;32m==>\e[0m\033[1m $* \e[m"
  47. }
  48. extract_href() {
  49. sed -n '/<a / s/^.*<a [^>]*href="\([^\"]*\)".*$/\1/p'
  50. }
  51. fetch() {
  52. curl -L -s "$@"
  53. }
  54. fetch_file() {
  55. local FILEPATH=$1
  56. shift
  57. if [[ -e "$FILEPATH" ]]; then
  58. curl -L -z "$FILEPATH" -o "$FILEPATH" "$@"
  59. else
  60. curl -L -o "$FILEPATH" "$@"
  61. fi
  62. }
  63. uncompress() {
  64. local FILEPATH=$1 DEST=$2
  65. case "$FILEPATH" in
  66. *.gz)
  67. tar xzf "$FILEPATH" -C "$DEST";;
  68. *.xz)
  69. tar -xf "$FILEPATH" -C "$DEST" > /dev/null 2> /dev/null;;
  70. *.lz)
  71. tar xf "$FILEPATH" -C "$DEST";;
  72. *.zst)
  73. zstd -dc "$FILEPATH" | tar x -C "$DEST";;
  74. *)
  75. debug "Error: unknown package format: $FILEPATH"
  76. return 1;;
  77. esac
  78. }
  79. ###
  80. get_default_repo() {
  81. local ARCH=$1
  82. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  83. echo $DEFAULT_REPO_URL
  84. fi
  85. }
  86. get_core_repo_url() {
  87. local REPO_URL=$1 ARCH=$2
  88. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  89. echo "${REPO_URL%/}/core/os/$ARCH"
  90. fi
  91. }
  92. get_extra_repo_url() {
  93. local REPO_URL=$1 ARCH=$2
  94. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  95. echo "${REPO_URL%/}/extra/os/$ARCH"
  96. fi
  97. }
  98. get_community_repo_url() {
  99. local REPO_URL=$1 ARCH=$2
  100. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  101. echo "${REPO_URL%/}/community/os/$ARCH"
  102. fi
  103. }
  104. get_template_repo_url() {
  105. local REPO_URL=$1 ARCH=$2
  106. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  107. echo "${REPO_URL%/}/\$repo/os/$ARCH"
  108. fi
  109. }
  110. configure_pacman() {
  111. local DEST=$1 ARCH=$2
  112. debug "Configuring SERVER"
  113. SERVER=$(get_template_repo_url "$REPO_URL" "$ARCH")
  114. debug "Configuring CERT"
  115. cp -fv certs/1.pem "$DEST/etc/ca-certificates/extracted/tls-ca-bundle.pem"
  116. }
  117. clean_chroot() {
  118. local DEST=$1
  119. debug "Clean Chroot"
  120. rm -rf "$DEST/.BUILDINFO" "$DEST/.INSTALL" "$DEST/.MTREE" "$DEST/.PKGINFO" || true
  121. echo '' > "$DEST/var/log/pacman.log"
  122. }
  123. configure_minimal_system() {
  124. local DEST=$1
  125. mkdir -p "$DEST/dev"
  126. sed -ie 's|^root:.*$|root:$1$GT9AUpJe$oXANVIjIzcnmOpY07iaGi/:14657::::::|' "$DEST/etc/shadow"
  127. touch "$DEST/etc/group"
  128. echo "bootstrap" > "$DEST/etc/hostname"
  129. rm -f "$DEST/etc/mtab"
  130. echo "rootfs / rootfs rw 0 0" > "$DEST/etc/mtab"
  131. test -e "$DEST/dev/null" || mknod "$DEST/dev/null" c 1 3
  132. test -e "$DEST/dev/random" || mknod -m 0644 "$DEST/dev/random" c 1 8
  133. test -e "$DEST/dev/urandom" || mknod -m 0644 "$DEST/dev/urandom" c 1 9
  134. sed -i "s|^[[:space:]]*\(CheckSpace\)|# \1|" "$DEST/etc/pacman.conf"
  135. sed -i "s|^[[:space:]]*SigLevel[[:space:]]*=.*$|SigLevel = Never|" "$DEST/etc/pacman.conf"
  136. }
  137. configure_locale() {
  138. local DEST=$1
  139. sed -e 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' -i "$DEST/etc/locale.gen"
  140. echo LANG=en_US.UTF-8 > "$DEST/etc/locale.conf"
  141. LC_ALL=C chroot "$DEST" \
  142. locale-gen
  143. }
  144. fetch_packages_list() {
  145. local REPO=$1
  146. debug "Fetch packages list: $REPO/"
  147. fetch "$REPO/" | extract_href | awk -F"/" '{print $NF}' | sort -rn ||
  148. { debug "Error: cannot fetch packages list: $REPO"; return 1; }
  149. }
  150. install_pacman_packages() {
  151. local BASIC_PACKAGES=$1 DEST=$2 LIST=$3 DOWNLOAD_DIR=$4
  152. debug "pacman package and dependencies: $BASIC_PACKAGES"
  153. for PACKAGE in $BASIC_PACKAGES; do
  154. local FILE=$(echo "$LIST" | grep -m1 "^$PACKAGE-[[:digit:]].*\(\.gz\|\.xz\|\.lz\|\.zst\)$")
  155. test "$FILE" || { debug "Error: cannot find package: $PACKAGE"; return 1; }
  156. local FILEPATH="$DOWNLOAD_DIR/$FILE"
  157. debug "Download package: $REPO/$FILE"
  158. fetch_file "$FILEPATH" "$REPO/$FILE"
  159. debug "Uncompress package: $FILEPATH"
  160. uncompress "$FILEPATH" "$DEST"
  161. done
  162. }
  163. install_pacman_packages_community() {
  164. local BASIC_PACKAGES=$1 DEST=$2 LIST=$3 DOWNLOAD_DIR=$4
  165. debug "pacman package and dependencies: $COMMUNITY_PACKAGES"
  166. for PACKAGE in $BASIC_PACKAGES; do
  167. local FILE=$(echo "$LIST" | grep -m1 "^$PACKAGE-[[:digit:]].*\(\.gz\|\.xz\|\.lz\|\.zst\)$")
  168. test "$FILE" || { debug "Error: cannot find package: $PACKAGE"; return 1; }
  169. local FILEPATH="$DOWNLOAD_DIR/$FILE"
  170. debug "Download package: $REPO_COMMUNITY/$FILE"
  171. fetch_file "$FILEPATH" "$REPO_COMMUNITY/$FILE"
  172. debug "Uncompress package: $FILEPATH"
  173. uncompress "$FILEPATH" "$DEST"
  174. done
  175. }
  176. configure_static_qemu() {
  177. local ARCH=$1 DEST=$2
  178. [[ "$ARCH" == arm* ]] && ARCH=arm
  179. QEMU_STATIC_BIN=$(command -v qemu-$ARCH-static || echo )
  180. [[ -e "$QEMU_STATIC_BIN" ]] ||\
  181. { debug "No static qemu for $ARCH, ignoring"; return 0; }
  182. cp "$QEMU_STATIC_BIN" "$DEST/usr/bin"
  183. }
  184. install_packages() {
  185. local ARCH=$1 DEST=$2 PACKAGES=$3
  186. debug "Install packages: $PACKAGES"
  187. LC_ALL=C chroot "$DEST" \
  188. /usr/bin/pacman --noconfirm --noprogressbar --quiet --arch $ARCH -Syy --force $PACKAGES \
  189. && /usr/bin/pacman --noconfirm --noprogressbar --quiet -Scc
  190. }
  191. configure_keyring() {
  192. local DEST=$1
  193. sed -i 's|SigLevel = Never|SigLevel = Required DatabaseOptional|' "$DEST/etc/pacman.conf"
  194. LC_ALL=C chroot "$DEST" \
  195. /usr/bin/pacman-key --init && /usr/bin/pacman-key --populate archlinux hyperbola \
  196. && /usr/bin/pacman-key --refresh-keys \
  197. && /usr/bin/pacman -Sy hyperbola-keyring --noconfirm --noprogressbar --quiet
  198. }
  199. show_usage() {
  200. stderr "Usage: $(basename "$0") [-q] [-a i686|x86_64|arm] [-r REPO_URL] [-d DOWNLOAD_DIR] DESTDIR"
  201. }
  202. main() {
  203. # Process arguments and options
  204. test $# -eq 0 && set -- "-h"
  205. local ARCH=
  206. local REPO_URL=
  207. local USE_QEMU=
  208. local DOWNLOAD_DIR=
  209. local PRESERVE_DOWNLOAD_DIR=
  210. while getopts "qa:r:d:h" ARG; do
  211. case "$ARG" in
  212. a) ARCH=$OPTARG;;
  213. r) REPO_URL=$OPTARG;;
  214. q) USE_QEMU=true;;
  215. d) DOWNLOAD_DIR=$OPTARG
  216. PRESERVE_DOWNLOAD_DIR=true;;
  217. *) show_usage; return 1;;
  218. esac
  219. done
  220. shift $(($OPTIND-1))
  221. test $# -eq 1 || { show_usage; return 1; }
  222. [[ -z "$ARCH" ]] && ARCH=$(uname -m)
  223. [[ -z "$REPO_URL" ]] && REPO_URL=$(get_default_repo "$ARCH")
  224. local DEST=$1
  225. local REPO=$(get_core_repo_url "$REPO_URL" "$ARCH")
  226. local REPO_COMMUNITY=$(get_community_repo_url "$REPO_URL" "$ARCH")
  227. [[ -z "$DOWNLOAD_DIR" ]] && DOWNLOAD_DIR=$(mktemp -d)
  228. mkdir -p "$DOWNLOAD_DIR"
  229. [[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && trap "rm -rf '$DOWNLOAD_DIR'" TERM EXIT
  230. debug "Destination directory: $DEST"
  231. debug "Core repository: $REPO"
  232. debug "Temporary directory: $DOWNLOAD_DIR"
  233. # Fetch packages, install system and do a minimal configuration
  234. mkdir -p "$DEST"
  235. local LIST_1=$(fetch_packages_list $REPO)
  236. local LIST_3=$(fetch_packages_list $REPO_COMMUNITY)
  237. install_pacman_packages "${CORE_PACKAGES[*]}" "$DEST" "$LIST_1" "$DOWNLOAD_DIR"
  238. install_pacman_packages_community "${COMMUNITY_PACKAGES[*]}" "$DEST" "$LIST_3" "$DOWNLOAD_DIR"
  239. configure_pacman "$DEST" "$ARCH"
  240. configure_minimal_system "$DEST"
  241. [[ -n "$USE_QEMU" ]] && configure_static_qemu "$ARCH" "$DEST"
  242. install_packages "$ARCH" "$DEST" "${CORE_PACKAGES[*]} ${EXTRA_PACKAGES[*]}"
  243. configure_locale "$DEST"
  244. configure_keyring "$DEST"
  245. clean_chroot "$DEST" # clean
  246. [[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && rm -rf "$DOWNLOAD_DIR"
  247. debug "Done!"
  248. debug
  249. debug "You may now chroot or arch-chroot from package arch-install-scripts:"
  250. debug "$ doas arch-chroot $DEST"
  251. }
  252. main "$@"