hyper-bootstrap_v0.4.2.sh 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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:50011/gnu-plus-linux-libre/testing destination-64
  12. #
  13. # Example:
  14. #
  15. # # ./hyper-bootstrap.sh -a x86_64 -r "https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/testing" 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 lzip grep sed coreutils
  33. libassuan libgpg-error libnghttp2 libssh2 lzo libressl pacman pacman-mirrorlist xz zlib libffi
  34. krb5 e2fsprogs keyutils libidn2 gcc-libs lz4 libpsl icu zstd readline libunistring findutils
  35. ncurses pinentry-curses lsb-release ca-certificates ca-certificates-mozilla p11-kit libtasn1
  36. libcap shadow pcre gzip gettext-tiny
  37. )
  38. BASIC_PACKAGES=(${PACMAN_PACKAGES[*]} filesystem)
  39. EXTRA_PACKAGES=(gawk file tar hyperrc)
  40. DEFAULT_REPO_URL="https://mirror.fsf.org/hyperbola/gnu-plus-linux-libre/testing"
  41. stderr() {
  42. echo "$@" >&2
  43. }
  44. debug() {
  45. echo -e "\e[1;32m==>\e[0m\033[1m $* \e[m"
  46. }
  47. extract_href() {
  48. sed -n '/<a / s/^.*<a [^>]*href="\([^\"]*\)".*$/\1/p'
  49. }
  50. fetch() {
  51. curl -L -s "$@"
  52. }
  53. fetch_file() {
  54. local FILEPATH=$1
  55. shift
  56. if [[ -e "$FILEPATH" ]]; then
  57. curl -L -z "$FILEPATH" -o "$FILEPATH" "$@"
  58. else
  59. curl -L -o "$FILEPATH" "$@"
  60. fi
  61. }
  62. uncompress() {
  63. local FILEPATH=$1 DEST=$2
  64. case "$FILEPATH" in
  65. *.gz)
  66. tar xzf "$FILEPATH" -C "$DEST";;
  67. *.xz)
  68. xz -dc "$FILEPATH" | tar x -C "$DEST";;
  69. *.lz)
  70. tar xf "$FILEPATH" -C "$DEST";;
  71. *.zst)
  72. zstd -dc "$FILEPATH" | tar x -C "$DEST";;
  73. *)
  74. debug "Error: unknown package format: $FILEPATH"
  75. return 1;;
  76. esac
  77. }
  78. ###
  79. get_default_repo() {
  80. local ARCH=$1
  81. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  82. echo $DEFAULT_REPO_URL
  83. fi
  84. }
  85. get_core_repo_url() {
  86. local REPO_URL=$1 ARCH=$2
  87. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  88. echo "${REPO_URL%/}/core/os/$ARCH"
  89. fi
  90. }
  91. get_template_repo_url() {
  92. local REPO_URL=$1 ARCH=$2
  93. if [[ "$ARCH" == x86* || "$ARCH" == i686 ]]; then
  94. echo "${REPO_URL%/}/\$repo/os/$ARCH"
  95. fi
  96. }
  97. configure_pacman() {
  98. local DEST=$1 ARCH=$2
  99. debug "Configuring SERVER"
  100. SERVER=$(get_template_repo_url "$REPO_URL" "$ARCH")
  101. echo "Server = $SERVER" > "$DEST/etc/pacman.d/mirrorlist"
  102. debug "Configuring CERT"
  103. cp -fv certs/1.pem "$DEST/etc/ca-certificates/extracted/tls-ca-bundle.pem"
  104. }
  105. clean_chroot() {
  106. local DEST=$1
  107. debug "Clean Chroot"
  108. rm -rf "$DEST/.BUILDINFO" "$DEST/.INSTALL" "$DEST/.MTREE" "$DEST/.PKGINFO" || true
  109. for i in $DEST/usr/share/{doc,man,info}; do
  110. rm -rf "$i" && install -d "$i"
  111. done
  112. truncate -s 0 "$DEST/var/log/pacman.log"
  113. }
  114. configure_minimal_system() {
  115. local DEST=$1
  116. mkdir -p "$DEST/dev"
  117. sed -ie 's|^root:.*$|root:$1$GT9AUpJe$oXANVIjIzcnmOpY07iaGi/:14657::::::|' "$DEST/etc/shadow"
  118. touch "$DEST/etc/group"
  119. echo "nameserver 9.9.9.9" > "$DEST/etc/resolv.conf"
  120. echo "bootstrap" > "$DEST/etc/hostname"
  121. rm -f "$DEST/etc/mtab"
  122. echo "rootfs / rootfs rw 0 0" > "$DEST/etc/mtab"
  123. test -e "$DEST/dev/null" || mknod "$DEST/dev/null" c 1 3
  124. test -e "$DEST/dev/random" || mknod -m 0644 "$DEST/dev/random" c 1 8
  125. test -e "$DEST/dev/urandom" || mknod -m 0644 "$DEST/dev/urandom" c 1 9
  126. sed -i "s|^[[:space:]]*\(CheckSpace\)|# \1|" "$DEST/etc/pacman.conf"
  127. }
  128. restore_signature() {
  129. local DEST=$1
  130. sed -i "s|SigLevel = Never|SigLevel = Required DatabaseOptional|" "$DEST/etc/pacman.conf"
  131. }
  132. configure_locale() {
  133. local DEST=$1
  134. sed -e 's/^#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g' -i "$DEST/etc/locale.gen"
  135. echo LANG=en_US.UTF-8 > "$DEST/etc/locale.conf"
  136. LC_ALL=C chroot "$DEST" \
  137. locale-gen
  138. }
  139. fetch_packages_list() {
  140. local REPO=$1
  141. debug "Fetch packages list: $REPO/"
  142. fetch "$REPO/" | extract_href | awk -F"/" '{print $NF}' | sort -rn ||
  143. { debug "Error: cannot fetch packages list: $REPO"; return 1; }
  144. }
  145. install_pacman_packages() {
  146. local BASIC_PACKAGES=$1 DEST=$2 LIST=$3 DOWNLOAD_DIR=$4
  147. debug "pacman package and dependencies: $BASIC_PACKAGES"
  148. for PACKAGE in $BASIC_PACKAGES; do
  149. local FILE=$(echo "$LIST" | grep -m1 "^$PACKAGE-[[:digit:]].*\(\.gz\|\.xz\|\.lz\|\.zst\)$")
  150. test "$FILE" || { debug "Error: cannot find package: $PACKAGE"; return 1; }
  151. local FILEPATH="$DOWNLOAD_DIR/$FILE"
  152. debug "Download package: $REPO/$FILE"
  153. fetch_file "$FILEPATH" "$REPO/$FILE"
  154. debug "Uncompress package: $FILEPATH"
  155. uncompress "$FILEPATH" "$DEST"
  156. done
  157. }
  158. configure_static_qemu() {
  159. local ARCH=$1 DEST=$2
  160. [[ "$ARCH" == arm* ]] && ARCH=arm
  161. QEMU_STATIC_BIN=$(command -v qemu-$ARCH-static || echo )
  162. [[ -e "$QEMU_STATIC_BIN" ]] ||\
  163. { debug "No static qemu for $ARCH, ignoring"; return 0; }
  164. cp "$QEMU_STATIC_BIN" "$DEST/usr/bin"
  165. }
  166. install_packages() {
  167. local ARCH=$1 DEST=$2 PACKAGES=$3
  168. debug "Install packages: $PACKAGES"
  169. LC_ALL=C chroot "$DEST" \
  170. /usr/bin/pacman --noconfirm --noprogressbar --quiet --arch $ARCH -Syy --force $PACKAGES \
  171. && /usr/bin/pacman --noconfirm --noprogressbar --quiet -Scc
  172. }
  173. show_usage() {
  174. stderr "Usage: $(basename "$0") [-q] [-a i686|x86_64|arm] [-r REPO_URL] [-d DOWNLOAD_DIR] DESTDIR"
  175. }
  176. main() {
  177. # Process arguments and options
  178. test $# -eq 0 && set -- "-h"
  179. local ARCH=
  180. local REPO_URL=
  181. local USE_QEMU=
  182. local DOWNLOAD_DIR=
  183. local PRESERVE_DOWNLOAD_DIR=
  184. while getopts "qa:r:d:h" ARG; do
  185. case "$ARG" in
  186. a) ARCH=$OPTARG;;
  187. r) REPO_URL=$OPTARG;;
  188. q) USE_QEMU=true;;
  189. d) DOWNLOAD_DIR=$OPTARG
  190. PRESERVE_DOWNLOAD_DIR=true;;
  191. *) show_usage; return 1;;
  192. esac
  193. done
  194. shift $(($OPTIND-1))
  195. test $# -eq 1 || { show_usage; return 1; }
  196. [[ -z "$ARCH" ]] && ARCH=$(uname -m)
  197. [[ -z "$REPO_URL" ]] && REPO_URL=$(get_default_repo "$ARCH")
  198. local DEST=$1
  199. local REPO=$(get_core_repo_url "$REPO_URL" "$ARCH")
  200. [[ -z "$DOWNLOAD_DIR" ]] && DOWNLOAD_DIR=$(mktemp -d)
  201. mkdir -p "$DOWNLOAD_DIR"
  202. [[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && trap "rm -rf '$DOWNLOAD_DIR'" TERM EXIT
  203. debug "Destination directory: $DEST"
  204. debug "Core repository: $REPO"
  205. debug "Temporary directory: $DOWNLOAD_DIR"
  206. # Fetch packages, install system and do a minimal configuration
  207. mkdir -p "$DEST"
  208. local LIST=$(fetch_packages_list $REPO)
  209. install_pacman_packages "${BASIC_PACKAGES[*]}" "$DEST" "$LIST" "$DOWNLOAD_DIR"
  210. configure_pacman "$DEST" "$ARCH"
  211. configure_minimal_system "$DEST"
  212. [[ -n "$USE_QEMU" ]] && configure_static_qemu "$ARCH" "$DEST"
  213. install_packages "$ARCH" "$DEST" "${BASIC_PACKAGES[*]} ${EXTRA_PACKAGES[*]}"
  214. configure_locale "$DEST"
  215. clean_chroot "$DEST" # clean
  216. [[ -z "$PRESERVE_DOWNLOAD_DIR" ]] && rm -rf "$DOWNLOAD_DIR"
  217. restore_signature "$DEST"
  218. debug "Done!"
  219. debug
  220. debug "You may now chroot or arch-chroot from package arch-install-scripts:"
  221. debug "$ doas arch-chroot $DEST"
  222. }
  223. main "$@"