Basic code for OS image packaging script

This commit is contained in:
fiatrete
2023-05-11 11:25:09 +08:00
parent 434242d027
commit 97c908fc5b
11 changed files with 1390 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# Build Scripts
## build.sh
```
This script builds a bootable ubuntu ISO image
Supported commands : setup_host debootstrap run_chroot build_iso
Syntax: ./build.sh [start_cmd] [-] [end_cmd]
run from start_cmd to end_end
if start_cmd is omitted, start from first command
if end_cmd is omitted, end with last command
enter single cmd to run the specific command
enter '-' as only argument to run all commands
```
## How to Customize
1. Copy the `default_config.sh` file to `config.sh` in the scripts directory.
2. Make any necessary edits there, the script will pick up `config.sh` over `default_config.sh`.
## How to Update
The configuration script is versioned with the variable CONFIG_FILE_VERSION. Any time that the configuration
format is changed in `default_config.sh`, this value is bumped. Once this happens `config.sh` must be updated manually
from the default file to ensure the new/changed variables are as desired. Once the merge is complete the `config.sh` file's
CONFIG_FILE_VERSION should match the default and the build will run.
+319
View File
@@ -0,0 +1,319 @@
#!/bin/bash
set -e # exit on error
set -o pipefail # exit on pipeline error
set -u # treat unset variable as error
#set -x
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
CMD=(setup_host debootstrap run_chroot build_iso)
DATE=`TZ="UTC" date +"%y%m%d-%H%M%S"`
function help() {
# if $1 is set, use $1 as headline message in help()
if [ -z ${1+x} ]; then
echo -e "This script builds a bootable ubuntu ISO image"
echo -e
else
echo -e $1
echo
fi
echo -e "Supported commands : ${CMD[*]}"
echo -e
echo -e "Syntax: $0 [start_cmd] [-] [end_cmd]"
echo -e "\trun from start_cmd to end_end"
echo -e "\tif start_cmd is omitted, start from first command"
echo -e "\tif end_cmd is omitted, end with last command"
echo -e "\tenter single cmd to run the specific command"
echo -e "\tenter '-' as only argument to run all commands"
echo -e
exit 0
}
function find_index() {
local ret;
local i;
for ((i=0; i<${#CMD[*]}; i++)); do
if [ "${CMD[i]}" == "$1" ]; then
index=$i;
return;
fi
done
help "Command not found : $1"
}
function chroot_enter_setup() {
sudo mount --bind /dev chroot/dev
sudo mount --bind /run chroot/run
sudo chroot chroot mount none -t proc /proc
sudo chroot chroot mount none -t sysfs /sys
sudo chroot chroot mount none -t devpts /dev/pts
}
function chroot_exit_teardown() {
sudo chroot chroot umount /proc
sudo chroot chroot umount /sys
sudo chroot chroot umount /dev/pts
sudo umount chroot/dev
sudo umount chroot/run
}
function check_host() {
local os_ver
os_ver=`lsb_release -i | grep -E "(Ubuntu|Debian)"`
if [[ -z "$os_ver" ]]; then
echo "WARNING : OS is not Debian or Ubuntu and is untested"
fi
#if [ $(id -u) -eq 0 ]; then
# echo "This script should not be run as 'root'"
# exit 1
#fi
}
# Load configuration values from file
function load_config() {
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
. "$SCRIPT_DIR/config.sh"
elif [[ -f "$SCRIPT_DIR/default_config.sh" ]]; then
. "$SCRIPT_DIR/default_config.sh"
else
>&2 echo "Unable to find default config file $SCRIPT_DIR/default_config.sh, aborting."
exit 1
fi
}
# Verify that necessary configuration values are set and they are valid
function check_config() {
local expected_config_version
expected_config_version="0.4"
if [[ "$CONFIG_FILE_VERSION" != "$expected_config_version" ]]; then
>&2 echo "Invalid or old config version $CONFIG_FILE_VERSION, expected $expected_config_version. Please update your configuration file from the default."
exit 1
fi
}
function setup_host() {
echo "=====> running setup_host ..."
sudo apt update
sudo apt install -y binutils debootstrap squashfs-tools xorriso grub-pc-bin grub-efi-amd64-bin mtools dosfstools unzip
sudo mkdir -p chroot
}
function debootstrap() {
echo "=====> running debootstrap ... will take a couple of minutes ..."
sudo debootstrap --arch=amd64 --variant=minbase $TARGET_UBUNTU_VERSION chroot $TARGET_UBUNTU_MIRROR
}
function run_chroot() {
echo "=====> running run_chroot ..."
chroot_enter_setup
# Setup build scripts in chroot environment
sudo ln -f $SCRIPT_DIR/chroot_build.sh chroot/root/chroot_build.sh
sudo ln -f $SCRIPT_DIR/default_config.sh chroot/root/default_config.sh
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
sudo ln -f $SCRIPT_DIR/config.sh chroot/root/config.sh
fi
# Launch into chroot environment to build install image.
sudo chroot chroot /usr/bin/env DEBIAN_FRONTEND=${DEBIAN_FRONTEND:-readline} /root/chroot_build.sh -
# Cleanup after image changes
sudo rm -f chroot/root/chroot_build.sh
sudo rm -f chroot/root/default_config.sh
if [[ -f "chroot/root/config.sh" ]]; then
sudo rm -f chroot/root/config.sh
fi
chroot_exit_teardown
}
function build_iso() {
echo "=====> running build_iso ..."
rm -rf image
mkdir -p image/{casper,isolinux,install}
# copy kernel files
sudo cp chroot/boot/vmlinuz-**-**-generic image/casper/vmlinuz
sudo cp chroot/boot/initrd.img-**-**-generic image/casper/initrd
# memtest86
sudo cp chroot/boot/memtest86+.bin image/install/memtest86+
wget --progress=dot https://www.memtest86.com/downloads/memtest86-usb.zip -O image/install/memtest86-usb.zip
unzip -p image/install/memtest86-usb.zip memtest86-usb.img > image/install/memtest86
rm -f image/install/memtest86-usb.zip
# grub
touch image/ubuntu
cat <<EOF > image/isolinux/grub.cfg
search --set=root --file /ubuntu
insmod all_video
set default="0"
set timeout=30
menuentry "${GRUB_LIVEBOOT_LABEL}" {
linux /casper/vmlinuz boot=casper nopersistent toram quiet splash ---
initrd /casper/initrd
}
menuentry "${GRUB_INSTALL_LABEL}" {
linux /casper/vmlinuz boot=casper only-ubiquity quiet splash ---
initrd /casper/initrd
}
menuentry "Check disc for defects" {
linux /casper/vmlinuz boot=casper integrity-check quiet splash ---
initrd /casper/initrd
}
menuentry "Test memory Memtest86+ (BIOS)" {
linux16 /install/memtest86+
}
menuentry "Test memory Memtest86 (UEFI, long load time)" {
insmod part_gpt
insmod search_fs_uuid
insmod chain
loopback loop /install/memtest86
chainloader (loop,gpt1)/efi/boot/BOOTX64.efi
}
EOF
# generate manifest
sudo chroot chroot dpkg-query -W --showformat='${Package} ${Version}\n' | sudo tee image/casper/filesystem.manifest
sudo cp -v image/casper/filesystem.manifest image/casper/filesystem.manifest-desktop
for pkg in $TARGET_PACKAGE_REMOVE; do
sudo sed -i "/$pkg/d" image/casper/filesystem.manifest-desktop
done
# compress rootfs
sudo mksquashfs chroot image/casper/filesystem.squashfs \
-noappend -no-duplicates -no-recovery \
-wildcards \
-e "var/cache/apt/archives/*" \
-e "root/*" \
-e "root/.*" \
-e "tmp/*" \
-e "tmp/.*" \
-e "swapfile"
printf $(sudo du -sx --block-size=1 chroot | cut -f1) > image/casper/filesystem.size
# create diskdefines
cat <<EOF > image/README.diskdefines
#define DISKNAME ${GRUB_LIVEBOOT_LABEL}
#define TYPE binary
#define TYPEbinary 1
#define ARCH amd64
#define ARCHamd64 1
#define DISKNUM 1
#define DISKNUM1 1
#define TOTALNUM 0
#define TOTALNUM0 1
EOF
# create iso image
pushd $SCRIPT_DIR/image
grub-mkstandalone \
--format=x86_64-efi \
--output=isolinux/bootx64.efi \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=isolinux/grub.cfg"
(
cd isolinux && \
dd if=/dev/zero of=efiboot.img bs=1M count=10 && \
sudo mkfs.vfat efiboot.img && \
LC_CTYPE=C mmd -i efiboot.img efi efi/boot && \
LC_CTYPE=C mcopy -i efiboot.img ./bootx64.efi ::efi/boot/
)
grub-mkstandalone \
--format=i386-pc \
--output=isolinux/core.img \
--install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \
--modules="linux16 linux normal iso9660 biosdisk search" \
--locales="" \
--fonts="" \
"boot/grub/grub.cfg=isolinux/grub.cfg"
cat /usr/lib/grub/i386-pc/cdboot.img isolinux/core.img > isolinux/bios.img
sudo /bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v -e 'md5sum.txt' -e 'bios.img' -e 'efiboot.img' > md5sum.txt)"
sudo xorriso \
-as mkisofs \
-iso-level 3 \
-full-iso9660-filenames \
-volid "$TARGET_NAME" \
-eltorito-boot boot/grub/bios.img \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--eltorito-catalog boot/grub/boot.cat \
--grub2-boot-info \
--grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \
-eltorito-alt-boot \
-e EFI/efiboot.img \
-no-emul-boot \
-append_partition 2 0xef isolinux/efiboot.img \
-output "$SCRIPT_DIR/$TARGET_NAME.iso" \
-m "isolinux/efiboot.img" \
-m "isolinux/bios.img" \
-graft-points \
"/EFI/efiboot.img=isolinux/efiboot.img" \
"/boot/grub/bios.img=isolinux/bios.img" \
"."
popd
}
# ============= main ================
# we always stay in $SCRIPT_DIR
cd $SCRIPT_DIR
load_config
check_config
check_host
# check number of args
if [[ $# == 0 || $# > 3 ]]; then help; fi
# loop through args
dash_flag=false
start_index=0
end_index=${#CMD[*]}
for ii in "$@";
do
if [[ $ii == "-" ]]; then
dash_flag=true
continue
fi
find_index $ii
if [[ $dash_flag == false ]]; then
start_index=$index
else
end_index=$(($index+1))
fi
done
if [[ $dash_flag == false ]]; then
end_index=$(($start_index + 1))
fi
#loop through the commands
for ((ii=$start_index; ii<$end_index; ii++)); do
${CMD[ii]}
done
echo "$0 - Initial build is done!"
+222
View File
@@ -0,0 +1,222 @@
#!/bin/bash
set -e # exit on error
set -o pipefail # exit on pipeline error
set -u # treat unset variable as error
#set -x
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
CMD=(setup_host install_pkg finish_up)
NVIDIA_DRIVER_VERSION=530
function help() {
# if $1 is set, use $1 as headline message in help()
if [ -z ${1+x} ]; then
echo -e "This script builds Ubuntu from scratch"
echo -e
else
echo -e $1
echo
fi
echo -e "Supported commands : ${CMD[*]}"
echo -e
echo -e "Syntax: $0 [start_cmd] [-] [end_cmd]"
echo -e "\trun from start_cmd to end_end"
echo -e "\tif start_cmd is omitted, start from first command"
echo -e "\tif end_cmd is omitted, end with last command"
echo -e "\tenter single cmd to run the specific command"
echo -e "\tenter '-' as only argument to run all commands"
echo -e
exit 0
}
function find_index() {
local ret;
local i;
for ((i=0; i<${#CMD[*]}; i++)); do
if [ "${CMD[i]}" == "$1" ]; then
index=$i;
return;
fi
done
help "Command not found : $1"
}
function check_host() {
if [ $(id -u) -ne 0 ]; then
echo "This script should be run as 'root'"
exit 1
fi
export HOME=/root
export LC_ALL=C
export DEBIAN_FRONTEND=noninteractive
}
function setup_host() {
echo "=====> running setup_host ..."
cat <<EOF > /etc/apt/sources.list
deb $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION main restricted universe multiverse
deb-src $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION main restricted universe multiverse
deb $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION-security main restricted universe multiverse
deb-src $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION-security main restricted universe multiverse
deb $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION-updates main restricted universe multiverse
deb-src $TARGET_UBUNTU_MIRROR $TARGET_UBUNTU_VERSION-updates main restricted universe multiverse
EOF
echo "$TARGET_NAME" > /etc/hostname
# we need to install systemd first, to configure machine id
apt-get update
apt-get install -y libterm-readline-gnu-perl systemd-sysv
#configure machine id
dbus-uuidgen > /etc/machine-id
ln -fs /etc/machine-id /var/lib/dbus/machine-id
# don't understand why, but multiple sources indicate this
dpkg-divert --local --rename --add /sbin/initctl
ln -s /bin/true /sbin/initctl
}
# Load configuration values from file
function load_config() {
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
. "$SCRIPT_DIR/config.sh"
elif [[ -f "$SCRIPT_DIR/default_config.sh" ]]; then
. "$SCRIPT_DIR/default_config.sh"
else
>&2 echo "Unable to find default config file $SCRIPT_DIR/default_config.sh, aborting."
exit 1
fi
}
function install_pkg() {
echo "=====> running install_pkg ... will take a long time ..."
apt-get -y upgrade
# install live packages
apt-get install -y \
sudo \
ubuntu-standard \
casper \
discover \
laptop-detect \
os-prober \
network-manager \
resolvconf \
net-tools \
wireless-tools \
wpagui \
grub-common \
grub-gfxpayload-lists \
grub-pc \
grub-pc-bin \
grub2-common \
locales \
ubuntu-drivers-common
# install nvidia drivers
ubuntu-drivers install nvidia:${NVIDIA_DRIVER_VERSION}
case $TARGET_UBUNTU_VERSION in
"focal" | "bionic")
apt-get install -y lupin-casper
;;
*)
echo "Package lupin-casper is not needed. Skipping."
;;
esac
# install kernel
apt-get install -y --no-install-recommends $TARGET_KERNEL_PACKAGE
# graphic installer - ubiquity
apt-get install -y \
ubiquity \
ubiquity-casper \
ubiquity-frontend-gtk \
ubiquity-slideshow-ubuntu \
ubiquity-ubuntu-artwork
# Call into config function
customize_image
# remove unused and clean up apt cache
apt-get autoremove -y
# final touch
dpkg-reconfigure locales
dpkg-reconfigure resolvconf
# network manager
cat <<EOF > /etc/NetworkManager/NetworkManager.conf
[main]
rc-manager=resolvconf
plugins=ifupdown,keyfile
dns=dnsmasq
[ifupdown]
managed=false
EOF
dpkg-reconfigure network-manager
apt-get clean -y
}
function finish_up() {
echo "=====> finish_up"
# truncate machine id (why??)
truncate -s 0 /etc/machine-id
# remove diversion (why??)
rm /sbin/initctl
dpkg-divert --rename --remove /sbin/initctl
rm -rf /tmp/* ~/.bash_history
}
# ============= main ================
load_config
check_host
# check number of args
if [[ $# == 0 || $# > 3 ]]; then help; fi
# loop through args
dash_flag=false
start_index=0
end_index=${#CMD[*]}
for ii in "$@";
do
if [[ $ii == "-" ]]; then
dash_flag=true
continue
fi
find_index $ii
if [[ $dash_flag == false ]]; then
start_index=$index
else
end_index=$(($index+1))
fi
done
if [[ $dash_flag == false ]]; then
end_index=$(($start_index + 1))
fi
# loop through the commands
for ((ii=$start_index; ii<$end_index; ii++)); do
${CMD[ii]}
done
echo "$0 - Initial build is done!"
@@ -0,0 +1,73 @@
#!/bin/bash
# This script provides common customization options for the ISO
#
# Usage: Copy this file to config.sh and make changes there. Keep this file (default_config.sh) as-is
# so that subsequent changes can be easily merged from upstream. Keep all customiations in config.sh
# The version of Ubuntu to generate. Successfully tested: bionic, cosmic, disco, eoan, focal, groovy, jammy
# See https://wiki.ubuntu.com/DevelopmentCodeNames for details
export TARGET_UBUNTU_VERSION="jammy"
# The Ubuntu Mirror URL. It's better to change for faster download.
# More mirrors see: https://launchpad.net/ubuntu/+archivemirrors
export TARGET_UBUNTU_MIRROR="http://mirrors.ustc.edu.cn/ubuntu/"
# The packaged version of the Linux kernel to install on target image.
# See https://wiki.ubuntu.com/Kernel/LTSEnablementStack for details
export TARGET_KERNEL_PACKAGE="linux-generic"
# The file (no extension) of the ISO containing the generated disk image,
# the volume id, and the hostname of the live environment are set from this name.
export TARGET_NAME="ubuntu-opendan"
# The text label shown in GRUB for booting into the live environment
export GRUB_LIVEBOOT_LABEL="Try OpenDan Ubuntu FS without installing"
# The text label shown in GRUB for starting installation
export GRUB_INSTALL_LABEL="Install OpenDan Ubuntu FS"
# Packages to be removed from the target system after installation completes succesfully
export TARGET_PACKAGE_REMOVE="
ubiquity \
casper \
discover \
laptop-detect \
os-prober \
"
# Package customisation function. Update this function to customize packages
# present on the installed system.
function customize_image() {
# install graphics and desktop
apt-get install -y \
plymouth-theme-ubuntu-logo \
ubuntu-gnome-desktop \
ubuntu-gnome-wallpapers
# useful tools
apt-get install -y \
clamav-daemon \
terminator \
apt-transport-https \
curl \
vim \
nano \
less
# purge
apt-get purge -y \
transmission-gtk \
transmission-common \
gnome-mahjongg \
gnome-mines \
gnome-sudoku \
aisleriot \
hitori
}
# Used to version the configuration. If breaking changes occur, manual
# updates to this file from the default may be necessary.
export CONFIG_FILE_VERSION="0.4"