root/build-tools/patch-iso-debian
Leonardo Fagundes Luz Serrano 69aadbb3c0 Remove patch-iso-debian's xmllint pkg requirement
With this change, if 'xmllint' is not available,
the script will try to use python3's xml default module

Other changes:
- Corrected misleading logs regarding what info
  is extracted from the patch xmls
- Added support for interchangeable requirements

Test Plan:
pass - Execute snippet with the changes and confirm
       ostree commit was properly extracted

Story: 2011098
Task: 49860

Change-Id: I13c2440814de337049c0af84f20b83bf524a05ea
Signed-off-by: Leonardo Fagundes Luz Serrano <Leonardo.FagundesLuzSerrano@windriver.com>
2024-05-29 17:26:09 +00:00

312 lines
7.9 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# Utility for adding patch metadata to the iso
# Debian patches are sequential and uses ostree
# so any patch will produce an updated iso, this
# utility injects the patch metadata into the iso.
# During install the kickstart will copy the metadata
# into the right location and the sw-patch query
# command will output the correct patch level
#
source "$(dirname $0)/image-utils.sh"
if [ -z "${STX_BUILD_HOME}" ]; then
echo "Required environment variable STX_BUILD_HOME is not set"
exit 1
fi
if [ -z "${MY_REPO}" ]; then
echo "Required environment variable MY_REPO is not set"
exit 1
fi
DEPLOY_DIR="${STX_BUILD_HOME}/localdisk/deploy"
OSTREE_REPO="${DEPLOY_DIR}/ostree_repo"
function usage() {
echo ""
echo "Usage: "
echo " $(basename $0) -i <input filename.iso> -o <output filename.iso> [ -p ] <patch> ..."
echo " -i <file>: Specify input ISO file"
echo " -o <file>: Specify output ISO file"
echo " -p <file>: Patch files. You can call it multiple times."
echo ""
}
function extract_ostree_commit_from_metadata_xml() {
local XML_PATH=$1
local XPATH="//contents/ostree/commit1/commit"
# Check if xmllint is available. Otherwise, use python's xml standard lib
if (which xmllint &>/dev/null); then
xmllint --xpath "string(${XPATH})" ${XML_PATH}
else
python3 -c "import xml.etree.ElementTree as ET ; print(ET.parse('${XML_PATH}').find('.${XPATH}').text, end='')"
fi
}
function extract_metadata() {
local patchesdir=${BUILDDIR}/patches
local patchfile=$1
local patchid=$(basename $patchfile .patch)
local ostree_log=$(ostree --repo=${OSTREE_REPO} log starlingx)
echo "Extracting ${patchfile}"
# Extract it
tar xf ${patchfile} -O metadata.tar | tar x -O > ${patchesdir}/${patchid}-metadata.xml
if [ $? -ne 0 ]; then
echo "Failed to extract metadata from ${patchfile}"
exit 1
fi
# Verify if top commit from metadata exist in ostree log
ostree_commit=$(extract_ostree_commit_from_metadata_xml ${patchesdir}/${patchid}-metadata.xml)
if [[ "$ostree_log" != *"$ostree_commit"* ]]; then
echo "Error: Patch ${patchid} ostree commit does not exist in ISO ostree log."
echo "patch's ostree commit: ${ostree_commit}"
echo "ISO ostree log:"
ostree --repo=${BUILDDIR}/ostree_repo log starlingx
exit 1
fi
}
declare INPUT_ISO=
declare OUTPUT_ISO=
declare ORIG_PWD=$PWD
declare DO_UPGRADES=1
while getopts "i:o:p:" opt; do
case $opt in
i)
INPUT_ISO=$OPTARG
;;
o)
OUTPUT_ISO=$OPTARG
;;
p)
PATCH_FILES+=($OPTARG)
;;
*)
usage
exit 1
;;
esac
done
if [ -z "$INPUT_ISO" -o -z "$OUTPUT_ISO" ]; then
usage
exit 1
fi
if [ ! -f ${INPUT_ISO} ]; then
echo "Input file does not exist: ${INPUT_ISO}"
exit 1
fi
if [ -f ${OUTPUT_ISO} ]; then
echo "Output file already exists: ${OUTPUT_ISO}"
exit 1
fi
for PATCH in "${PATCH_FILES[@]}";
do
if [ ! -f ${PATCH} ]; then
echo "Patch file dos not exists: ${PATCH}"
exit 1
fi
if [[ ! ${PATCH} =~ \.patch$ ]]; then
echo "Specified file ${PATCH} does not have .patch extension"
exit 1
fi
done
shift $((OPTIND-1))
declare MNTDIR=
declare BUILDDIR=
function check_requirements {
# Declare "require reqA or reqB" as "reqA__reqB"
local -a required_utils=(
rsync
mkisofs
isohybrid
implantisomd5
ostree
xmllint__python3
)
if [ $UID -ne 0 ]; then
# If running as non-root user, additional utils are required
required_utils+=(
guestmount
guestunmount
)
fi
local -i missing=0
local reqA
local reqB
for req in ${required_utils[@]}; do
if [[ "$req" = *"__"* ]]; then
reqA="${req%__*}" # select everything before "__"
reqB="${req#*__}" # select everything after "__"
if ! (which ${reqA} &>/dev/null) && ! (which ${reqB} &>/dev/null); then
echo "Unable to find required utility: either ${reqA} or ${reqB}" >&2
let missing++
fi
else
if ! (which ${req} &>/dev/null); then
echo "Unable to find required utility: ${req}" >&2
let missing++
fi
fi
done
if [ ${missing} -gt 0 ]; then
echo "One or more required utilities are missing. Aborting..." >&2
exit 1
fi
}
function mount_iso {
if [ $UID -eq 0 ]; then
# Mount the ISO
mount -o loop ${INPUT_ISO} ${MNTDIR}
if [ $? -ne 0 ]; then
echo "Failed to mount ${INPUT_ISO}" >&2
exit 1
fi
else
# As non-root user, mount the ISO using guestmount
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
rc=$?
if [ $rc -ne 0 ]; then
# Add a retry
echo "Call to guestmount failed with rc=$rc. Retrying once..."
guestmount -a ${INPUT_ISO} -m /dev/sda1 --ro ${MNTDIR}
rc=$?
if [ $rc -ne 0 ]; then
echo "Call to guestmount failed with rc=$rc. Aborting..."
exit $rc
fi
fi
fi
}
function unmount_iso {
if [ $UID -eq 0 ]; then
umount ${MNTDIR}
else
guestunmount ${MNTDIR}
fi
rmdir ${MNTDIR}
}
function cleanup() {
if [ -n "$MNTDIR" -a -d "$MNTDIR" ]; then
unmount_iso
fi
if [ -n "$BUILDDIR" -a -d "$BUILDDIR" ]; then
chmod -R +w $BUILDDIR
\rm -rf $BUILDDIR
fi
}
check_requirements
trap cleanup EXIT
MNTDIR=$(mktemp -d -p $PWD patchiso_mnt_XXXXXX)
if [ -z "${MNTDIR}" -o ! -d ${MNTDIR} ]; then
echo "Failed to create mntdir. Aborting..."
exit $rc
fi
BUILDDIR=$(mktemp -d -p $PWD patchiso_build_XXXXXX)
if [ -z "${BUILDDIR}" -o ! -d ${BUILDDIR} ]; then
echo "Failed to create builddir. Aborting..."
exit $rc
fi
# Mount the ISO
mount_iso
rsync -a --exclude 'ostree_repo' ${MNTDIR}/ ${BUILDDIR}/
rc=$?
if [ $rc -ne 0 ]; then
echo "Call to rsync ISO content. Aborting..."
exit $rc
fi
unmount_iso
# Fix for permission denied if not running as root
chmod +w ${BUILDDIR}
chmod -R +w ${BUILDDIR}/isolinux
# Create the directory where metadata will be stored
mkdir -p ${BUILDDIR}/patches
chmod -R +w ${BUILDDIR}/patches
echo "Copying only the latest commit from ostree_repo..."
ostree --repo=${BUILDDIR}/ostree_repo init --mode=archive-z2
ostree --repo=${BUILDDIR}/ostree_repo pull-local --depth=0 ${OSTREE_REPO} starlingx
ostree --repo=${BUILDDIR}/ostree_repo summary --update
echo "Updated iso ostree commit:"
ostree --repo=${BUILDDIR}/ostree_repo log starlingx
echo "Extracting patch metadata"
for PATCH in "${PATCH_FILES[@]}";
do
extract_metadata $PATCH
done
echo "Packing iso..."
# get the install label
ISO_LABEL=$(grep -ri instiso "${BUILDDIR}"/isolinux/isolinux.cfg | head -1 | xargs -n1 | awk -F= /instiso/'{print $2}')
if [ -z "${ISO_LABEL}" ] ; then
echo "Error: Failed to get iso install label"
fi
echo "ISO Label: ${ISO_LABEL}"
# Repack the ISO
mkisofs -o "${OUTPUT_ISO}" \
-A "${ISO_LABEL}" -V "${ISO_LABEL}" \
-quiet -U -J -joliet-long -r -iso-level 2 \
-b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot \
-boot-load-size 4 -boot-info-table \
-eltorito-alt-boot \
-e efi.img \
-no-emul-boot \
"${BUILDDIR}"
isohybrid --uefi ${OUTPUT_ISO}
implantisomd5 ${OUTPUT_ISO}
# Sign the .iso with the developer private key
openssl dgst -sha256 \
-sign ${MY_REPO}/build-tools/signing/dev-private-key.pem \
-binary \
-out ${OUTPUT_ISO/%.iso/.sig} \
${OUTPUT_ISO}
rc=$?
if [ $rc -ne 0 ]; then
echo "Call to $(basename ${SETUP_PATCH_REPO}) failed with rc=$rc. Aborting..."
exit $rc
fi
echo "Patched ISO: ${OUTPUT_ISO}"