283 lines
10 KiB
Bash
Executable File
283 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
# build.sh
|
|
# Automate Raspberry Pi image builds
|
|
# Take a config file on the command line and pass its options to the various stages of the build process
|
|
#
|
|
# Copyright 2018, F123 Consulting, <information@f123.org>
|
|
# Copyright 2018, Kyle, <kyle@free2.ml>
|
|
# Copyright 2018, Storm Dragon, <storm_dragon@linux-a11y.org>
|
|
#
|
|
# This is free software; you can redistribute it and/or modify it under the
|
|
# terms of the GNU General Public License as published by the Free
|
|
# Software Foundation; either version 3, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This software is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this package; see the file COPYING. If not, write to the Free
|
|
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
# 02110-1301, USA.
|
|
#
|
|
#--code--
|
|
|
|
export TEXTDOMAIN=build.sh
|
|
export TEXTDOMAINDIR=./locale
|
|
|
|
. gettext.sh
|
|
|
|
# Log writing function
|
|
log() {
|
|
# Usage: command | log for just stdout.
|
|
# Or command |& log for stderr and stdout.
|
|
while read -r line ; do
|
|
echo "$line" | sudo tee -a "$logFile"
|
|
done
|
|
}
|
|
|
|
# Log file name is /var/log/build.stormux
|
|
logFile="/var/log/build.stormux"
|
|
|
|
set -e
|
|
# Load the common functions
|
|
. scripts/functions
|
|
|
|
# Die if not running as root
|
|
if ! check_root ; then
|
|
PROGNAME=$0
|
|
echo $(eval_gettext "Script must be run as root, try: sudo \$PROGNAME")
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
# Clear previous logs
|
|
echo -n | sudo tee "$logFile" &> /dev/null
|
|
|
|
declare -A argList=(
|
|
[d:]="work-directory:,"
|
|
[H:]="hostname:,"
|
|
[h]="help,"
|
|
[n:]="network-name:,"
|
|
[o:]="output-file:,"
|
|
[p:]="password:,"
|
|
[r:]="root-password:,"
|
|
[s:]="size:,"
|
|
[u:]="user:,"
|
|
[w:]="wifi-password:"
|
|
)
|
|
|
|
short="${!argList[*]}"
|
|
short="${short// /}"
|
|
long="${argList[*]}"
|
|
long="${long// /}"
|
|
|
|
|
|
# Get command line options
|
|
if ! options=$(getopt -o $short -l $long -n "build.sh" -- "$@"); then
|
|
echo "Unknown options passed to $0" | log
|
|
exit 1
|
|
fi
|
|
|
|
eval set -- "$options"
|
|
|
|
# Declare the overrides that will replace configuration file variables with command line options
|
|
declare -A overrides=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-d|--work-directory) overrides[workdir]="$2"; shift 2 ;;
|
|
-H|--hostname) overrides[hostname]="$2"; shift 2 ;;
|
|
-h|--help) cat <<-HELP
|
|
$(gettext "Usage")
|
|
$(eval_gettext "\$name [options...]")
|
|
$(gettext "Options")
|
|
-d, --work-directory: $(gettext "The work directory for caching packages and mounting filesystems")
|
|
-H, --hostname: $(gettext "The hostname of the installed system")
|
|
-h, --help: $(gettext "Print this help and exit")
|
|
-n, --network-name: $(gettext "The name of the wifi network. If specified, a wifi password is also required.")
|
|
-o, --output-file: $(gettext "The name of the image file to write")
|
|
-p, --password: $(gettext "The password of the regular non-root user")
|
|
-r, --root-password: $(gettext "The password of the root user")
|
|
-s --size: $(gettext "The initial size of the image file in MB")
|
|
-u, --user: $(gettext "The name of the regular non-root user")
|
|
-w, --wifi-password: $(gettext "the password used to login to the specified wifi network, requires network name")
|
|
HELP
|
|
exit 0 ;;
|
|
-n|--network-name) overrides[networkname]="$2"; shift 2 ;;
|
|
-o|--output-file) overrides[imagename]="$2"; shift 2 ;;
|
|
-p|--password) overrides[userpass]="$2"; shift 2 ;;
|
|
-r|--root-password) overrides[rootpass]="$2"; shift 2 ;;
|
|
-s|--size) overrides[imagesize]="$2"; shift 2 ;;
|
|
-u|--user) overrides[username]="$2"; shift 2 ;;
|
|
-w|--wifi-password) overrides[wifipass]="$2"; shift 2 ;;
|
|
(--) shift ;;
|
|
(*)
|
|
if [[ -f "$1" ]]; then
|
|
source "$1"
|
|
else
|
|
echo "Invalid configuration file specified." | log
|
|
exit 1
|
|
fi
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Replace configuration variables using the overrides gathered from command line options
|
|
if [[ "${overrides[workdir]}" ]]; then
|
|
workdir="${overrides[workdir]}"
|
|
fi
|
|
if [[ "${overrides[hostname]}" ]]; then
|
|
hostname="${overrides[hostname]}"
|
|
fi
|
|
if [[ "${overrides[networkname]}" ]]; then
|
|
networkname="${overrides[networkname]}"
|
|
fi
|
|
if [[ "${overrides[imagename]}" ]]; then
|
|
imagename="${PWD}/${overrides[imagename]}"
|
|
fi
|
|
if [[ "${overrides[userpass]}" ]]; then
|
|
userpass="${overrides[userpass]}"
|
|
fi
|
|
if [[ "${overrides[rootpass]}" ]]; then
|
|
rootpass="${overrides[rootpass]}"
|
|
fi
|
|
if [[ "${overrides[imagesize]}" ]]; then
|
|
imagesize="${overrides[imagesize]}"
|
|
fi
|
|
if [[ "${overrides[username]}" ]]; then
|
|
username="${overrides[username]}"
|
|
fi
|
|
if [[ "${overrides[wifipass]}" ]]; then
|
|
wifipass="${overrides[wifipass]}"
|
|
fi
|
|
|
|
# export workdir so it is available in subshells.
|
|
export workdir
|
|
|
|
# All build stage scripts run from the scripts directory underneath the directory where this build script resides.
|
|
# todo: Call the loaded functions as needed rather than calling the stage scripts.
|
|
# First, the current directory must be saved.
|
|
current=$PWD
|
|
|
|
# Now we enter the directory where the stage scripts live.
|
|
cd scripts && echo "cd $PWD" | log
|
|
|
|
# Check for dependencies and install any that are missing.
|
|
check_deps
|
|
|
|
# Before building an image, we must be sure that the work directory is clean except for a package cache.
|
|
# There is not yet a facility to complete a half-built failed image, but we can cache software packages.
|
|
if [ -d "$workdir" ]; then
|
|
clean_up "$workdir" || true
|
|
# Don't remove an otherwise empty work directory if a pacman cache is present
|
|
[[ -d "${workdir}/pacman-cache" ]] || rm -Rv "$workdir" |& log
|
|
fi
|
|
|
|
# Start by creating an empty image and mounting it.
|
|
new_image $workdir $imagename $bootlabel $rootlabel boot root $imagesize 1M 64M |& log
|
|
|
|
# Install packages available from the official ALARM repositories.
|
|
./pacstrap -l "$packagelist" -c "${workdir}/pacman-cache" "$workdir/root" |& log
|
|
|
|
# Set the system hostname
|
|
echo $hostname > $workdir/root/etc/hostname
|
|
|
|
# The root partition is automatically mounted by systemd. Add only the boot partition information to fstab.
|
|
echo -e "\n/dev/mmcblk0p1 /boot vfat defaults 0 0" >> $workdir/root/etc/fstab
|
|
|
|
# Set the root password
|
|
set_password "${workdir}/root" root "${rootpass}"
|
|
|
|
# Install locale specific packages. The file name looks like the configured package list file, but includes an @ sign followed by the locale followed by .list, e.g. packages@sw_KE.list.
|
|
# This file will be ignored if it doesn't exist.
|
|
test -f "${packagelist%%.list}@${locale}.list" && ./pacstrap -l "${packagelist%%.list}@${locale}.list" -c "${workdir}/pacman-cache" "$workdir/root"
|
|
|
|
# Add pulseaudio stuff and other groups
|
|
add_groups "${workdir}/root" |& log
|
|
|
|
# Add the regular user and set its password
|
|
add_user "${workdir}/root" "${username}" |& log
|
|
set_password "${workdir}/root" "${username}" "${userpass}" |& log
|
|
|
|
# Copy any override files into the new system. This overrides any system and user configuration files or scripts installed with packages.
|
|
# Remove any stale temporary files related to the file and content repositories
|
|
rm -Rfv /tmp/stormux |& log
|
|
|
|
# Start by cloning the stormux repository to /tmp
|
|
git clone -b $(git branch | grep \* | cut -f2 -d \ ) --recurse-submodules https://gitlab.com/stormux/stormux.git /tmp/stormux |& log
|
|
|
|
# Copy in the files.
|
|
cp -Rv /tmp/stormux/files/files/* "${workdir}/root/" |& log
|
|
# Generate translations:
|
|
find /tmp/stormux/files/locales -type f -iname '*.po' -exec bash -c '
|
|
for i ; do
|
|
# i is in file, o is outfile.
|
|
language="$(grep "^\"Language: .*\\n" "${i}" | cut -d " " -f2 | cut -d "\\" -f1)"
|
|
# Handle exceptions for languages.
|
|
case "$language" in
|
|
*) language="${language::2}";;
|
|
esac
|
|
o="${i##*/}"
|
|
o="${o%.po}.mo"
|
|
msgfmt "$i" -o "${workdir}/root/usr/share/locale/${language}/LC_MESSAGES/${o}"
|
|
done' _ {} +
|
|
mkdir -p "${workdir}/root/usr/share/doc/stormux" |& log
|
|
cp -Rv /tmp/stormux/content/stormux/* "${workdir}/root/usr/share/doc/stormux/" |& log
|
|
|
|
# Set the system locale and generate all other locales specified in the configuration file.
|
|
# The default system locale must be set before we can copy user files to $HOME.
|
|
setup_locales $workdir/root $defaultlocale ${locales[@]} |& log
|
|
|
|
# Always copy the contents of /etc/skel to the home directory of the user that was created earlier
|
|
systemd-nspawn -a -q -D $workdir/root\
|
|
sudo -u \#1000\
|
|
bash -c "shopt -s dotglob && cp -R /etc/skel/* \"\$HOME\"" |& log
|
|
|
|
# Enable system services
|
|
for service in $services; do
|
|
manage_service "${workdir}/root" enable "${service}" |& log
|
|
done
|
|
|
|
# Globally enable dbus-broker
|
|
systemd-nspawn -a -q -D $workdir/root\
|
|
systemctl --global enable dbus-broker.service |& log
|
|
|
|
# Download package databases
|
|
download_package_databases $workdir/root |& log
|
|
set_fake_hwclock $workdir/root |& log
|
|
|
|
# Configure wifi if network information is present in the configuration file.
|
|
if [[ -n "$networkname" ]]; then
|
|
setup_wifi $workdir/root wlan0 $networkname $wifipass |& log
|
|
fi
|
|
|
|
# Touch the firstboot file. It will be deleted by the firstboot script that looks for it.
|
|
systemd-nspawn -a -q -D $workdir/root\
|
|
sudo -u \#1000\
|
|
bash -c "touch \$HOME/.firstboot"
|
|
|
|
# Write a system timestamp.
|
|
# This timestamp will determine which incremental updates apply to this image
|
|
echo "$(date +%y%m%d%H%M)" > $workdir/root/etc/timestamp-stormux
|
|
|
|
# Unmount the image file
|
|
clean_up "$workdir" |& log
|
|
# Keep the work directory if a pacman cache exists, otherwise remove it
|
|
if [[ ! -d "${workdir}/pacman-cache" ]]; then
|
|
rm -Rv "$workdir" |& log
|
|
fi
|
|
|
|
# Once all scripts have completed, come back to the directory from which this script was launched.
|
|
cd $current && echo "cd $current" |& log
|
|
|
|
newrootsize=$(ls -hs "${imagename}" | cut -f1 -d' ')
|
|
relativeimage=$(realpath --relative-to="$PWD" "$imagename")
|
|
echo "$relativeimage was built successfully" | log
|
|
echo "Size of $relativeimage is ${newrootsize}" | log
|
|
|
|
exit 0
|