2022-03-19 14:34:36 -01:00
#!/bin/bash
# Rebuild an ISO image for a given timestamp
#
# Copyright 2021-2022 Holger Levsen <holger@layer-acht.org>
2023-01-28 16:08:13 -01:00
# Copyright 2021-2023 Roland Clobus <rclobus@rclobus.nl>
2022-03-19 14:34:36 -01:00
# released under the GPLv2
# Command line arguments:
# 1) Image type
# 2) Debian version
2022-07-24 19:26:08 +00:00
# 3) [optional] argument for the timestamp:
# - 'archive' (default): fetches the timestamp from the Debian archive
# - 'snapshot': fetches the latest timestamp from the snapshot server
# - A timestamp (format: YYYYMMDD'T'HHMMSS'Z'): a specific timestamp on the snapshot server
2022-11-12 10:46:45 -01:00
# 4) [optional] argument for the origin of the d-i:
# - 'git' (default): rebuild the installer from git
# - 'archive': take the installer from the Debian archive
2022-03-19 14:34:36 -01:00
# Environment variables:
# http_proxy: The proxy that is used by live-build and wget
# https_proxy: The proxy that is used by git
# SNAPSHOT_TIMESTAMP: The timestamp to rebuild (format: YYYYMMDD'T'HHMMSS'Z')
2023-01-28 16:08:13 -01:00
# This script can be run as root, but root rights are only required for a few commands.
# You are advised to configure the user with 'visudo' instead.
2022-03-19 14:34:36 -01:00
# Required entries in the sudoers file:
# Defaults env_keep += "SOURCE_DATE_EPOCH"
# Defaults env_keep += "LIVE_BUILD"
# thisuser ALL=(root) NOPASSWD: /usr/bin/lb build
# thisuser ALL=(root) NOPASSWD: /usr/bin/lb clean --purge
# Coding convention: enforced by 'shfmt'
DEBUG = false
set -e
set -o pipefail # see eg http://petereisentraut.blogspot.com/2010/11/pipefail.html
output_echo( ) {
set +x
echo "###########################################################################################"
echo
echo -e " $( date -u) - $1 "
echo
if $DEBUG ; then
set -x
fi
}
cleanup( ) {
output_echo " Generating summary.txt $1 "
cat <<EOF >summary.txt
Configuration: ${ CONFIGURATION }
Debian version: ${ DEBIAN_VERSION }
2022-07-24 19:26:08 +00:00
Use latest snapshot: ${ BUILD_LATEST_DESC }
2022-11-12 10:46:45 -01:00
Installer origin: ${ INSTALLER_ORIGIN }
2022-03-19 14:34:36 -01:00
Snapshot timestamp: ${ SNAPSHOT_TIMESTAMP }
Snapshot epoch: ${ SOURCE_DATE_EPOCH }
Live-build override: ${ LIVE_BUILD_OVERRIDE }
Live-build path: ${ LIVE_BUILD }
Build result: ${ BUILD_RESULT }
Alternative timestamp: ${ PROPOSED_SNAPSHOT_TIMESTAMP }
Checksum: ${ SHA256SUM }
EOF
2022-07-24 19:26:08 +00:00
touch summary.txt -d@${ SOURCE_DATE_EPOCH }
2022-03-19 14:34:36 -01:00
}
parse_commandline_arguments( ) {
# Argument 1 = image type
case $1 in
"smallest-build" )
2023-01-28 19:52:52 -01:00
INSTALLER = "none"
PACKAGES = ""
2022-03-19 14:34:36 -01:00
; ;
"cinnamon" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-cinnamon"
2022-03-19 14:34:36 -01:00
; ;
"gnome" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-gnome"
2022-03-19 14:34:36 -01:00
; ;
"kde" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-kde"
2022-03-19 14:34:36 -01:00
; ;
"lxde" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-lxde"
2022-03-19 14:34:36 -01:00
; ;
"lxqt" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
2023-06-04 10:25:50 +00:00
PACKAGES = "live-task-lxqt"
2022-03-19 14:34:36 -01:00
; ;
"mate" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-mate"
2022-03-19 14:34:36 -01:00
; ;
"standard" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-standard"
2022-03-19 14:34:36 -01:00
; ;
"xfce" )
2023-01-28 19:52:52 -01:00
INSTALLER = "live"
PACKAGES = "live-task-xfce"
2022-03-19 14:34:36 -01:00
; ;
*)
output_echo " Error: Bad argument 1, image type: $1 "
exit 1
; ;
esac
2023-01-28 19:52:52 -01:00
CONFIGURATION = " $1 "
2022-03-19 14:34:36 -01:00
# Argument 2 = Debian version
# Use 'stable', 'testing' or 'unstable' or code names like 'sid'
if [ -z " $2 " ] ; then
output_echo "Error: Bad argument 2, Debian version: it is empty"
exit 2
fi
2023-01-28 19:52:52 -01:00
DEBIAN_VERSION = " $2 "
2023-03-11 22:16:04 -01:00
case " $DEBIAN_VERSION " in
"bullseye" )
2023-03-14 18:28:32 -01:00
FIRMWARE_ARCHIVE_AREA = "non-free contrib"
2023-03-11 22:16:04 -01:00
; ;
*)
FIRMWARE_ARCHIVE_AREA = "non-free-firmware"
; ;
esac
2022-03-19 14:34:36 -01:00
# Argument 3 = optional timestamp
2023-01-28 19:52:52 -01:00
BUILD_LATEST = "archive"
BUILD_LATEST_DESC = "yes, from the main Debian archive"
2022-03-19 14:34:36 -01:00
if [ ! -z " $3 " ] ; then
2022-07-24 19:26:08 +00:00
case $3 in
"archive" )
2023-01-28 19:52:52 -01:00
BUILD_LATEST = "archive"
BUILD_LATEST_DESC = "yes, from the main Debian archive"
2022-07-24 19:26:08 +00:00
; ;
"snapshot" )
2023-01-28 19:52:52 -01:00
BUILD_LATEST = "snapshot"
BUILD_LATEST_DESC = "yes, from the snapshot server"
2022-07-24 19:26:08 +00:00
; ;
*)
2023-01-28 19:52:52 -01:00
SNAPSHOT_TIMESTAMP = $3
2022-07-24 19:26:08 +00:00
BUILD_LATEST = "no"
BUILD_LATEST_DESC = "no"
; ;
esac
2022-03-19 14:34:36 -01:00
fi
2022-11-12 10:46:45 -01:00
2023-01-28 19:52:52 -01:00
INSTALLER_ORIGIN = "git"
2022-11-12 10:46:45 -01:00
if [ ! -z " $4 " ] ; then
case $4 in
"git" )
2023-01-28 19:52:52 -01:00
INSTALLER_ORIGIN = "git"
2022-11-12 10:46:45 -01:00
; ;
"archive" )
2023-01-28 19:52:52 -01:00
INSTALLER_ORIGIN = " ${ DEBIAN_VERSION } "
2022-11-12 10:46:45 -01:00
; ;
*)
output_echo " Error: Bad argument 4, unknown value ' $4 ' provided "
exit 4
; ;
esac
fi
2022-03-19 14:34:36 -01:00
}
2022-07-24 19:26:08 +00:00
get_snapshot_from_archive( ) {
wget ${ WGET_OPTIONS } http://deb.debian.org/debian/dists/${ DEBIAN_VERSION } /InRelease --output-document latest
#
# Extract the timestamp from the InRelease file
#
# Input:
# ...
# Date: Sat, 23 Jul 2022 14:33:45 UTC
# ...
# Output:
# 20220723T143345Z
#
2023-01-28 19:52:52 -01:00
SNAPSHOT_TIMESTAMP = $( cat latest | awk '/^Date:/ { print substr($0, 7) }' | xargs -I this_date date --utc --date "this_date" +%Y%m%dT%H%M%SZ)
2022-07-24 19:26:08 +00:00
rm latest
}
2022-03-19 14:34:36 -01:00
#
# main: follow https://wiki.debian.org/ReproducibleInstalls/LiveImages
#
# Cleanup if something goes wrong
trap cleanup INT TERM EXIT
parse_commandline_arguments " $@ "
if $DEBUG ; then
2023-01-28 19:52:52 -01:00
WGET_OPTIONS =
GIT_OPTIONS =
2022-03-19 14:34:36 -01:00
else
2023-01-28 19:52:52 -01:00
WGET_OPTIONS = --quiet
GIT_OPTIONS = --quiet
2022-03-19 14:34:36 -01:00
fi
# No log required
WGET_OPTIONS = " ${ WGET_OPTIONS } --output-file /dev/null --timestamping "
if [ ! -z " ${ LIVE_BUILD } " ] ; then
2023-01-28 19:52:52 -01:00
LIVE_BUILD_OVERRIDE = 1
2022-03-19 14:34:36 -01:00
else
2023-01-28 19:52:52 -01:00
LIVE_BUILD_OVERRIDE = 0
2022-03-19 14:34:36 -01:00
export LIVE_BUILD = ${ PWD } /live-build
fi
2023-01-28 16:08:13 -01:00
# Prepend sudo for the commands that require it (when not running as root)
if [ " ${ EUID :- $( id -u) } " -ne 0 ] ; then
SUDO = sudo
fi
2022-03-19 14:34:36 -01:00
# Use a fresh git clone
if [ ! -d ${ LIVE_BUILD } -a ${ LIVE_BUILD_OVERRIDE } -eq 0 ] ; then
git clone https://salsa.debian.org/live-team/live-build.git ${ LIVE_BUILD } --single-branch --no-tags
fi
2023-01-28 19:52:52 -01:00
LB_OUTPUT = lb_output.txt
2022-03-19 14:34:36 -01:00
rm -f ${ LB_OUTPUT }
2022-07-24 19:26:08 +00:00
case ${ BUILD_LATEST } in
"archive" )
# Use the timestamp of the current Debian archive
get_snapshot_from_archive
2023-01-28 19:52:52 -01:00
MIRROR = http://deb.debian.org/debian/
2022-07-24 19:26:08 +00:00
; ;
"snapshot" )
2022-03-19 14:34:36 -01:00
# Use the timestamp of the latest mirror snapshot
wget ${ WGET_OPTIONS } http://snapshot.notset.fr/mr/timestamp/debian/latest --output-document latest
#
# Extract the timestamp from the JSON file
#
# Input:
# {
# "_api": "0.3",
# "_comment": "notset",
# "result": "20210828T083909Z"
# }
# Output:
# 20210828T083909Z
#
2023-01-28 19:52:52 -01:00
SNAPSHOT_TIMESTAMP = $( cat latest | awk '/"result":/ { split($0, a, "\""); print a[4] }' )
2022-03-19 14:34:36 -01:00
rm latest
2023-01-28 19:52:52 -01:00
MIRROR = http://snapshot.notset.fr/archive/debian/${ SNAPSHOT_TIMESTAMP }
2022-07-24 19:26:08 +00:00
; ;
"no" )
# The value of SNAPSHOT_TIMESTAMP was provided on the command line
2023-01-28 19:52:52 -01:00
MIRROR = http://snapshot.notset.fr/archive/debian/${ SNAPSHOT_TIMESTAMP }
2022-07-24 19:26:08 +00:00
; ;
*)
echo "E: A new option to BUILD_LATEST has been added"
exit 1
; ;
esac
2022-03-19 14:34:36 -01:00
# Convert SNAPSHOT_TIMESTAMP to Unix time (insert suitable formatting first)
export SOURCE_DATE_EPOCH = $( date -d $( echo ${ SNAPSHOT_TIMESTAMP } | awk '{ printf "%s-%s-%sT%s:%s:%sZ", substr($0,1,4), substr($0,5,2), substr($0,7,2), substr($0,10,2), substr($0,12,2), substr($0,14,2) }' ) +%s)
output_echo " Info: using the snapshot from ${ SOURCE_DATE_EPOCH } ( ${ SNAPSHOT_TIMESTAMP } ) "
# Use the code from the actual timestamp
# Report the versions that were actually used
if [ ${ LIVE_BUILD_OVERRIDE } -eq 0 ] ; then
pushd ${ LIVE_BUILD } >/dev/null
git pull ${ GIT_OPTIONS }
git checkout $( git rev-list -n 1 --min-age= ${ SOURCE_DATE_EPOCH } HEAD) ${ GIT_OPTIONS }
git clean -Xdf ${ GIT_OPTIONS }
output_echo " Info: using live-build from git version $( git log -n 1 --pretty= format:%H_%aI) "
popd >/dev/null
else
output_echo " Info: using local live-build: $( lb --version) "
fi
# If the configuration folder already exists, re-create from scratch
if [ -d config ] ; then
2023-01-28 16:08:13 -01:00
${ SUDO } lb clean --purge
2022-03-19 14:34:36 -01:00
rm -fr config
rm -fr .build
fi
# Configuration for the live image:
# - For /etc/apt/sources.list: Use the mirror from ${MIRROR}, no security, no updates
2022-05-02 18:04:47 +00:00
# - The debian-installer is built from its git repository
2022-03-19 14:34:36 -01:00
# - Don't cache the downloaded content
# - To reduce some network traffic a proxy is implicitly used
output_echo "Running lb config."
lb config \
2022-05-02 18:04:47 +00:00
--mirror-bootstrap ${ MIRROR } \
--mirror-binary ${ MIRROR } \
2022-03-19 14:34:36 -01:00
--security false \
--updates false \
--distribution ${ DEBIAN_VERSION } \
--debian-installer ${ INSTALLER } \
2022-11-12 10:46:45 -01:00
--debian-installer-distribution ${ INSTALLER_ORIGIN } \
2022-03-19 14:34:36 -01:00
--cache-packages false \
2023-03-11 22:16:04 -01:00
--archive-areas " main ${ FIRMWARE_ARCHIVE_AREA } " \
2022-03-19 14:34:36 -01:00
2>& 1 | tee $LB_OUTPUT
# Insider knowledge of live-build:
# Add '-o Acquire::Check-Valid-Until=false', to allow for rebuilds of older timestamps
sed -i -e '/^APT_OPTIONS=/s/--yes/--yes -o Acquire::Check-Valid-Until=false/' config/common
if [ ! -z " ${ PACKAGES } " ] ; then
echo " ${ PACKAGES } " >config/package-lists/desktop.list.chroot
fi
# Add additional hooks, that work around known issues regarding reproducibility
cp -a ${ LIVE_BUILD } /examples/hooks/reproducible/* config/hooks/normal
2023-03-25 12:39:47 -01:00
# For stable and soon-to-be-stable use the same boot splash screen as the Debian installer
case " $DEBIAN_VERSION " in
"bullseye" )
mkdir -p config/bootloaders/syslinux_common
wget --quiet https://salsa.debian.org/installer-team/debian-installer/-/raw/master/build/boot/artwork/11-homeworld/homeworld.svg -O config/bootloaders/syslinux_common/splash.svg
mkdir -p config/bootloaders/grub-pc
ln -s ../../isolinux/splash.png config/bootloaders/grub-pc/splash.png
; ;
"bookworm" )
mkdir -p config/bootloaders/syslinux_common
wget --quiet https://salsa.debian.org/installer-team/debian-installer/-/raw/master/build/boot/artwork/12-emerald/emerald.svg -O config/bootloaders/syslinux_common/splash.svg
mkdir -p config/bootloaders/grub-pc
ln -s ../../isolinux/splash.png config/bootloaders/grub-pc/splash.png
; ;
*)
# Use the default 'under construction' image
; ;
esac
2022-03-19 14:34:36 -01:00
# Build the image
output_echo "Running lb build."
set +e # We are interested in the result of 'lb build', so do not fail on errors
2023-01-28 16:08:13 -01:00
${ SUDO } lb build | tee -a $LB_OUTPUT
2023-01-28 19:52:52 -01:00
BUILD_RESULT = $?
2022-03-19 14:34:36 -01:00
set -e
if [ ${ BUILD_RESULT } -ne 0 ] ; then
# Find the snapshot that matches 1 second before the current snapshot
wget ${ WGET_OPTIONS } http://snapshot.notset.fr/mr/timestamp/debian/$( date --utc -d @$(( ${ SOURCE_DATE_EPOCH } - 1 )) +%Y%m%dT%H%M%SZ) --output-document but_latest
2023-01-28 19:52:52 -01:00
PROPOSED_SNAPSHOT_TIMESTAMP = $( cat but_latest | awk '/"result":/ { split($0, a, "\""); print a[4] }' )
2022-03-19 14:34:36 -01:00
rm but_latest
output_echo " Warning: lb build failed with ${ BUILD_RESULT } . The latest snapshot might not be complete (yet). Try re-running the script with SNAPSHOT_TIMESTAMP= ${ PROPOSED_SNAPSHOT_TIMESTAMP } . "
# Occasionally the snapshot is not complete, you could use the previous snapshot instead of giving up
exit 99
fi
# Calculate the checksum
2023-01-28 19:52:52 -01:00
SHA256SUM = $( sha256sum live-image-amd64.hybrid.iso | cut -f 1 -d " " )
2022-03-19 14:34:36 -01:00
2022-07-24 19:26:08 +00:00
if [ ${ BUILD_LATEST } = = "archive" ] ; then
2023-01-28 19:52:52 -01:00
SNAPSHOT_TIMESTAMP_OLD = ${ SNAPSHOT_TIMESTAMP }
2022-07-24 19:26:08 +00:00
get_snapshot_from_archive
if [ ${ SNAPSHOT_TIMESTAMP } != ${ SNAPSHOT_TIMESTAMP_OLD } ] ; then
output_echo "Warning: meanwhile the archive was updated. Try re-running the script."
2023-01-28 19:52:52 -01:00
PROPOSED_SNAPSHOT_TIMESTAMP = " ${ BUILD_LATEST } "
2022-07-24 19:26:08 +00:00
exit 99
fi
fi
2022-03-19 14:34:36 -01:00
cleanup success
# Turn off the trap
trap - INT TERM EXIT
# We reached the end, return with PASS
exit 0