deploy.sh 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #!/bin/sh
  2. set -xeu
  3. chroot_dir="/mnt"
  4. arch="$(uname -m)"
  5. mirror="https://dl-cdn.alpinelinux.org/alpine"
  6. alpine_version="v3.20"
  7. apk_version="2.14.4-r1"
  8. apk_link="${mirror}/${alpine_version}/main/${arch}/apk-tools-static-${apk_version}.apk"
  9. fatal() {
  10. echo "$1" >&2
  11. }
  12. die() {
  13. fatal "$2"
  14. exit "$1"
  15. }
  16. usage() {
  17. cat >&2 <<EOF
  18. Usage: $0 <action> [-r <chroot_dir>] [-h]
  19. Action:
  20. rootfs deploy alpine rootfs in <chroot_dir>
  21. setup setup rootfs inside chroot environment
  22. full deploy rootfs and run setup inside chroot
  23. Options:
  24. -r <chroot_dir> specify chroot directory
  25. [default: $chroot_dir]
  26. -h show this help message
  27. EOF
  28. exit "$1"
  29. }
  30. ensure_root() {
  31. if [ "$(id -u)" = 0 ]; then
  32. return 0
  33. fi
  34. die 1 "The script must be run as root"
  35. }
  36. parse_action() {
  37. case "$1" in
  38. rootfs|setup|full) action="$1" ;;
  39. *)
  40. fatal "unknown op: $1"
  41. usage 1
  42. ;;
  43. esac
  44. }
  45. parse_args() {
  46. [ "$#" -lt 1 ] && usage 1
  47. parse_action "$1" && shift
  48. while getopts "r:h" opt; do
  49. case "$opt" in
  50. r) chroot_dir="$(realpath "$OPTARG")" ;;
  51. h) usage 0 ;;
  52. ?) usage 1 ;;
  53. esac
  54. done
  55. }
  56. # download static apk and print the executable filename
  57. download_apk() {
  58. local tmpdir
  59. tmpdir="$(mktemp -d)"
  60. curl -L "$apk_link" | tar -C "$tmpdir" -xzf -
  61. echo "$tmpdir/sbin/apk.static"
  62. }
  63. install_rootfs() {
  64. apk_bin="$(download_apk)"
  65. mkdir -p "$chroot_dir/etc/apk"
  66. "$apk_bin" -X "$mirror/$alpine_version/main" -U --allow-untrusted \
  67. -p "$chroot_dir" --initdb add alpine-base e2fsprogs
  68. cat > "$chroot_dir/etc/resolv.conf" <<EOF
  69. nameserver 1.1.1.1
  70. nameserver 1.0.0.1
  71. nameserver 8.8.8.8
  72. EOF
  73. cat > "$chroot_dir/etc/apk/repositories" <<EOF
  74. $mirror/$alpine_version/main
  75. $mirror/$alpine_version/community
  76. EOF
  77. }
  78. umount_vfs_cleanup() {
  79. for fs in proc sys dev; do
  80. umount "$chroot_dir/$fs"
  81. done
  82. }
  83. bind_mount_vfs() {
  84. for fs in proc sys dev; do
  85. mount -o bind "/$fs" "$chroot_dir/$fs"
  86. done
  87. }
  88. install_inside_chroot() {
  89. deploy_name="$(realpath "$0")"
  90. inside_deploy_path="$chroot_dir/deploy.sh"
  91. cp "$deploy_name" "$inside_deploy_path"
  92. chmod +x "$inside_deploy_path"
  93. bind_mount_vfs
  94. trap umount_vfs_cleanup EXIT
  95. chroot "$chroot_dir" /deploy.sh setup
  96. trap - EXIT
  97. umount_vfs_cleanup
  98. }
  99. extra_setup_vim() {
  100. apk add vim
  101. ln -s "$(which vim)" /usr/local/bin/vi
  102. }
  103. extra_setup_fish() {
  104. apk add fish
  105. # chsh -s "$(which fish)"
  106. }
  107. setup_extra() {
  108. extra_setup_vim
  109. extra_setup_fish
  110. }
  111. install_kernel() {
  112. apk add linux-virt linux-firmware-none
  113. }
  114. install_packages() {
  115. apk add chrony acpi busybox-mdev-openrc
  116. apk add grub grub-bios
  117. apk add openssh blkid
  118. install_kernel
  119. }
  120. part_uuid() {
  121. blkid -o export -s UUID "$1" | sed -n -e 2p
  122. }
  123. mountpoint_dev() {
  124. mount | grep "on $1 " | awk '{ print $1 }'
  125. }
  126. mountpoint_fstype() {
  127. mount | grep "on $1 " | awk '{ print $5 }'
  128. }
  129. mountpoint_options() {
  130. mount | grep "on $1 " | awk '{ print $6 }' | tr -d '()'
  131. }
  132. # $1: mountpoint
  133. # $2: check on boot
  134. gen_fstab_entry() {
  135. _mnt="$1"
  136. _dev="$(mountpoint_dev "$_mnt")"
  137. _fs="$(mountpoint_fstype "$_mnt")"
  138. _options="$(mountpoint_options "$_mnt")"
  139. _uuid="$(part_uuid "$_dev")"
  140. case "$2" in
  141. y|Y|1) _check=1 ;;
  142. *) _check=0 ;;
  143. esac
  144. echo "$_uuid $_mnt $_fs $_options 0 $_check"
  145. }
  146. gen_fstab() {
  147. gen_fstab_entry / y
  148. echo "tmpfs /tmp tmpfs nosuid,nodev 0 0"
  149. }
  150. populate_fstab() {
  151. gen_fstab | tee /etc/fstab
  152. }
  153. enable_services() {
  154. rc-update add devfs sysinit
  155. rc-update add dmesg sysinit
  156. rc-update add mdev sysinit
  157. rc-update add hwclock boot
  158. rc-update add modules boot
  159. rc-update add sysctl boot
  160. rc-update add hostname boot
  161. rc-update add bootmisc boot
  162. rc-update add syslog boot
  163. rc-update add localmount boot
  164. rc-update add networking boot
  165. rc-update add mount-ro shutdown
  166. rc-update add killprocs shutdown
  167. rc-update add savecache shutdown
  168. rc-update add acpid default
  169. rc-update add crond default
  170. rc-update add chronyd default
  171. rc-update add sshd default
  172. }
  173. setup_rootfs() {
  174. install_packages
  175. enable_services
  176. echo "Set root password:"
  177. passwd
  178. setup_extra
  179. }
  180. full_install() {
  181. install_rootfs
  182. install_inside_chroot
  183. }
  184. parse_args "$@"
  185. ensure_root
  186. case "$action" in
  187. rootfs) install_rootfs ;;
  188. setup) setup_rootfs ;;
  189. full) full_install ;;
  190. *) usage 1;;
  191. esac