mirror of
https://github.com/armbian/build.git
synced 2025-09-13 09:41:11 +02:00
> tl-dr: > - maximize OCI cache hit ratio across nightlies/releases/PRs/etc; > - publish simple `Version:`'s that don't include a crazy hash in repo and images > - introduce `output/packages-hashed` directory > - radically change the `output/debs` directory structure - simplify artifact's `prepare_version()` method for `deb` and `deb-tar` artifacts: - `artifact_base_dir` and `artifact_final_file` will now be auto-calculated; thus removed from each artifact (except `rootfs`) - `artifact_deb_repo` ("global", "jammy", "bookworm") is now required; "global" means common across all RELEASES - `artifact_deb_arch` is now required, "all" is arch-independent, otherwise use `${ARCH}` - `artifact_map_debs` is now auto-calculated based on the above, and shouldn't be specified manually - `artifact_final_version_reversioned` is optional, and can force the final version of the artifact (specific for the `base-files` case) - artifacts that need special handling for reversioning can add function names to `artifact_debs_reversion_functions` array (`base-files` and `bsp-cli` cases) - artifacts `prepare_version()` should set `artifact_version`, but _never_ include it in other variables; `artifact_version` is now changed by framework after `prepare_version()` returns - no longer use/refer/mention `${REVISION}` when building packages. All packages should be `${REVISION}`-agnostic. - `${REVISION}` (actually, `artifact_final_version_reversioned`) will be automatically swapped in the `control` file during reversioning - `fakeroot_dpkg_deb_build()` now takes exactly two arguments: the directory to pack, and the deb ID (key of `artifact_map_packages` dict); add this change in all the artifact's code for this - `obtain_complete_artifact()`: - automatically adds `-Rxxxx` "revisioning-hash" to `artifact_version`, by hashing the revisioning functions and any `artifact_debs_reversion_functions` set - calculates more complex subdirectory paths for both the `output/packages-hashed` and `output/debs`/`output/debs-beta` directories - with the new subdirectories we can be sure a re-version is already done correctly and can skip it (eg, for partial `download-debs` re-runs) - in the future we can automatically clean/remove old versions that are no longer relevant based on the dir structure - exports a lot more information to JSON, including the new subdirectory paths - comment-out code that implemented `skip_unpack_if_found_in_caches`, I'm very unsure why we had this in the first place - `obtain_artifact_from_remote_cache()` - for `deb` type artifacts, OCI won't preserve the subdirectory structure, so move downloaded files to the correct subdirectory manually - this is not needed for `deb-tar`, since that can preserve the dir structure itself - introduce `artifacts-reversion.sh` and its main function `artifact_reversion_for_deployment()` - this has the logic for reversioning .deb's, by `ar`-unpacking them, changing `control.tar` (and possibly `data.tar`), handling `.xz` compression, etc. - also handles hashing those functions, for consistency. Any changes in reversioning code actually change the artifact itself so we're not caught by surprise - by default, it changes `control` file only: - replace `Version:` (which is the hash-version originally) with `artifact_final_version_reversioned` (which is mostly just `${REVISION}`) - add a custom field `Armbian-Original-Hash:` with the original hash-version - `artifact_reversion_for_deployment()` is called by - new CLI wrapper `cli_obtain_complete_artifact()`, used for CLI building of specific artifact, but also for `download-artifact` - `build_artifact_for_image()` used during image build - `armbian-bsp-cli-deb.sh`: move `${REVISION}` related stuff from the main package build to new reversioning functions. - `artifact-armbian-base-files.sh`: move `${REVISION}` related stuff from the main package build to new reversioning functions. - `kernel`: - add some custom fields to `DEBIAN/control`: - `Armbian-Kernel-Version:` / `Armbian-Kernel-Version-Family:` (for future use: cleanup of usage of `Source: ` field which should be removed) - declutter the `Description:` field, moving long description out of the first line - obtain `IMAGE_INSTALLED_KERNEL_VERSION` from the reversioned deb (this is still a hack and has not been fixed) - `uboot`: - declutter the `Description:` field, moving long description out of the first line - use the reversioned .deb when deploying u-boot to the image - `main_default_build_packages()` now stores reversioned values and complete paths to reversioned .deb's - `list_installed_packages()` now compares custom field `Armbian-Original-Hash: `, and not the `Version:` to make sure debs in the image are the ones we want - `install_artifact_deb_chroot()` is a new wrapper around `install_deb_chroot()` for easy handling of reversioned debs - use it everywhere `install_deb_chroot()` was used in `distro-agnostic.sh` and `distro-specific.sh`
226 lines
9.7 KiB
Bash
226 lines
9.7 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
#
|
|
# Copyright (c) 2013-2023 Igor Pecovnik, igor@armbian.com
|
|
#
|
|
# This file is a part of the Armbian Build Framework
|
|
# https://github.com/armbian/build/
|
|
|
|
function calculate_image_version() {
|
|
declare kernel_version_for_image="unknown"
|
|
kernel_version_for_image="${IMAGE_INSTALLED_KERNEL_VERSION/-$LINUXFAMILY/}"
|
|
|
|
declare vendor_version_prelude="${VENDOR}_${IMAGE_VERSION:-"${REVISION}"}_"
|
|
if [[ "${include_vendor_version:-"yes"}" == "no" ]]; then
|
|
vendor_version_prelude=""
|
|
fi
|
|
|
|
calculated_image_version="${vendor_version_prelude}${BOARD^}_${RELEASE}_${BRANCH}_${kernel_version_for_image}${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}${EXTRA_IMAGE_SUFFIX}"
|
|
[[ $BUILD_DESKTOP == yes ]] && calculated_image_version=${calculated_image_version}_desktop
|
|
[[ $BUILD_MINIMAL == yes ]] && calculated_image_version=${calculated_image_version}_minimal
|
|
[[ $ROOTFS_TYPE == nfs ]] && calculated_image_version=${calculated_image_version}_nfsboot
|
|
display_alert "Calculated image version" "${calculated_image_version}" "debug"
|
|
}
|
|
|
|
function create_image_from_sdcard_rootfs() {
|
|
# create DESTIMG, hooks might put stuff there early.
|
|
mkdir -p "${DESTIMG}"
|
|
|
|
# add a cleanup trap hook do make sure we don't leak it if stuff fails
|
|
add_cleanup_handler trap_handler_cleanup_destimg
|
|
|
|
# calculate image filename, and store it in readonly global variable "version", for legacy reasons.
|
|
declare calculated_image_version="undetermined"
|
|
calculate_image_version
|
|
declare -r -g version="${calculated_image_version}" # global readonly from here
|
|
declare rsync_ea=" -X "
|
|
# nilfs2 fs does not have extended attributes support, and have to be ignored on copy
|
|
if [[ $ROOTFS_TYPE == nilfs2 ]]; then rsync_ea=""; fi
|
|
if [[ $ROOTFS_TYPE != nfs ]]; then
|
|
display_alert "Copying files via rsync to" "/ (MOUNT root)"
|
|
run_host_command_logged rsync -aHWh $rsync_ea \
|
|
--exclude="/boot" \
|
|
--exclude="/dev/*" \
|
|
--exclude="/proc/*" \
|
|
--exclude="/run/*" \
|
|
--exclude="/tmp/*" \
|
|
--exclude="/sys/*" \
|
|
--info=progress0,stats1 $SDCARD/ $MOUNT/
|
|
else
|
|
display_alert "Creating rootfs archive" "rootfs.tgz" "info"
|
|
tar cp --xattrs --directory=$SDCARD/ --exclude='./boot/*' --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
|
|
--exclude='./sys/*' . |
|
|
pv -p -b -r -s "$(du -sb "$SDCARD"/ | cut -f1)" \
|
|
-N "$(logging_echo_prefix_for_pv "create_rootfs_archive") rootfs.tgz" |
|
|
gzip -c > "$DEST/images/${version}-rootfs.tgz"
|
|
fi
|
|
|
|
# stage: rsync /boot
|
|
display_alert "Copying files to" "/boot (MOUNT /boot)"
|
|
if [[ $(findmnt --noheadings --output FSTYPE --target "$MOUNT/boot" --uniq) == vfat ]]; then
|
|
# FAT filesystems can't have symlinks; rsync, below, will replace them with copies (-L)...
|
|
# ... unless they're dangling symlinks, in which case rsync will fail.
|
|
# Find dangling symlinks in "$MOUNT/boot", warn, and remove them.
|
|
display_alert "Checking for dangling symlinks" "in FAT32 /boot" "info"
|
|
declare -a dangling_symlinks=()
|
|
while IFS= read -r -d '' symlink; do
|
|
dangling_symlinks+=("$symlink")
|
|
done < <(find "$SDCARD/boot" -xtype l -print0)
|
|
if [[ ${#dangling_symlinks[@]} -gt 0 ]]; then
|
|
display_alert "Dangling symlinks in /boot" "$(printf '%s ' "${dangling_symlinks[@]}")" "warning"
|
|
run_host_command_logged rm -fv "${dangling_symlinks[@]}"
|
|
fi
|
|
run_host_command_logged rsync -rLtWh --info=progress0,stats1 "$SDCARD/boot" "$MOUNT" # fat32
|
|
else
|
|
run_host_command_logged rsync -aHWXh --info=progress0,stats1 "$SDCARD/boot" "$MOUNT" # ext4
|
|
fi
|
|
|
|
call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" <<- 'PRE_UPDATE_INITRAMFS'
|
|
*allow config to hack into the initramfs create process*
|
|
Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.
|
|
PRE_UPDATE_INITRAMFS
|
|
|
|
# stage: create final initramfs
|
|
[[ -n $KERNELSOURCE ]] && {
|
|
update_initramfs "$MOUNT"
|
|
}
|
|
|
|
# DEBUG: print free space @TODO this needs work, grepping might not be ideal here
|
|
local freespace
|
|
freespace=$(LC_ALL=C df -h || true) # don't break on failures
|
|
display_alert "Free SD cache" "$(echo -e "$freespace" | awk -v mp="${SDCARD}" '$6==mp {print $5}')" "info"
|
|
display_alert "Mount point" "$(echo -e "$freespace" | awk -v mp="${MOUNT}" '$6==mp {print $5}')" "info"
|
|
|
|
# stage: write u-boot, unless BOOTCONFIG=none
|
|
declare -g -A image_artifacts_debs_reversioned
|
|
if [[ "${BOOTCONFIG}" != "none" ]]; then
|
|
write_uboot_to_loop_image "${LOOP}" "${DEB_STORAGE}/${image_artifacts_debs_reversioned["uboot"]}"
|
|
fi
|
|
|
|
# fix wrong / permissions
|
|
chmod 755 "${MOUNT}"
|
|
|
|
call_extension_method "pre_umount_final_image" "config_pre_umount_final_image" <<- 'PRE_UMOUNT_FINAL_IMAGE'
|
|
*allow config to hack into the image before the unmount*
|
|
Called before unmounting both `/root` and `/boot`.
|
|
PRE_UMOUNT_FINAL_IMAGE
|
|
|
|
if [[ "${SHOW_DEBUG}" == "yes" ]]; then
|
|
# Check the partition table after the uboot code has been written
|
|
display_alert "Partition table after write_uboot" "$LOOP" "debug"
|
|
run_host_command_logged sfdisk -l "${LOOP}" # @TODO: use asset..
|
|
fi
|
|
|
|
wait_for_disk_sync "before umount MOUNT"
|
|
|
|
umount_chroot_recursive "${MOUNT}" "MOUNT"
|
|
[[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose "$ROOT_MAPPER"
|
|
|
|
call_extension_method "post_umount_final_image" "config_post_umount_final_image" <<- 'POST_UMOUNT_FINAL_IMAGE'
|
|
*allow config to hack into the image after the unmount*
|
|
Called after unmounting both `/root` and `/boot`.
|
|
POST_UMOUNT_FINAL_IMAGE
|
|
|
|
free_loop_device_insistent "${LOOP}"
|
|
unset LOOP # unset so cleanup handler does not try it again
|
|
|
|
# We're done with ${MOUNT} by now, remove it.
|
|
rm -rf --one-file-system "${MOUNT}"
|
|
# unset MOUNT # don't unset, it's readonly now
|
|
|
|
mkdir -p "${DESTIMG}"
|
|
# @TODO: misterious cwd, who sets it?
|
|
|
|
run_host_command_logged mv -v "${SDCARD}.raw" "${DESTIMG}/${version}.img"
|
|
|
|
# custom post_build_image_modify hook to run before fingerprinting and compression
|
|
[[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"
|
|
|
|
# Previously, post_build_image passed the .img path as an argument to the hook. Now its an ENV var.
|
|
declare -g FINAL_IMAGE_FILE="${DESTIMG}/${version}.img"
|
|
call_extension_method "post_build_image" <<- 'POST_BUILD_IMAGE'
|
|
*custom post build hook*
|
|
Called after the final .img file is built, before it is (possibly) written to an SD writer.
|
|
- *NOTE*: this hook used to take an argument ($1) for the final image produced.
|
|
- Now it is passed as an environment variable `${FINAL_IMAGE_FILE}`
|
|
It is the last possible chance to modify `$CARD_DEVICE`.
|
|
POST_BUILD_IMAGE
|
|
|
|
# Before compressing or moving, write it to SD card if such was requested and image was produced.
|
|
if [[ -f "${DESTIMG}/${version}.img" ]]; then
|
|
display_alert "Done building" "${version}.img" "info"
|
|
fingerprint_image "${DESTIMG}/${version}.img.txt" "${version}"
|
|
|
|
write_image_to_device_and_run_hooks "${DESTIMG}/${version}.img"
|
|
fi
|
|
|
|
declare compression_type # set by image_compress_and_checksum
|
|
output_images_compress_and_checksum "${DESTIMG}/${version}" # this compressed on-disk, and removes the originals.
|
|
|
|
# Move all files matching the prefix from source to dest. Custom hooks might generate more than one img.
|
|
declare source_dir="${DESTIMG}"
|
|
declare destination_dir="${FINALDEST}"
|
|
declare source_files_prefix="${version}"
|
|
move_images_to_final_destination
|
|
|
|
return 0
|
|
}
|
|
|
|
function write_image_to_device_and_run_hooks() {
|
|
if [[ ! -f "${1}" ]]; then
|
|
exit_with_error "Image file not found '${1}'"
|
|
fi
|
|
declare built_image_file="${1}"
|
|
|
|
# write image to SD card
|
|
write_image_to_device "${built_image_file}" "${CARD_DEVICE}"
|
|
|
|
# Hook: post_build_image_write
|
|
call_extension_method "post_build_image_write" <<- 'POST_BUILD_IMAGE_WRITE'
|
|
*custom post build hook*
|
|
Called after the final .img file is ready, and possibly written to an SD card.
|
|
The full path to the image is available in `${built_image_file}`.
|
|
POST_BUILD_IMAGE_WRITE
|
|
|
|
unset built_image_file
|
|
}
|
|
|
|
function move_images_to_final_destination() {
|
|
# validate that source_dir and destination_dir exist
|
|
[[ ! -d "${source_dir}" ]] && return 1
|
|
[[ ! -d "${destination_dir}" ]] && return 2
|
|
|
|
declare -a source_files=("${source_dir}/${source_files_prefix}."*)
|
|
if [[ ${#source_files[@]} -eq 0 ]]; then
|
|
display_alert "No files to deploy" "${source_dir}/${source_files_prefix}.*" "wrn"
|
|
fi
|
|
|
|
# if source_dir and destination_dir are on the same filesystem. use stat to get the device number
|
|
declare source_dir_device
|
|
declare destination_dir_device
|
|
source_dir_device=$(stat -c %d "${source_dir}")
|
|
destination_dir_device=$(stat -c %d "${destination_dir}")
|
|
display_alert "source_dir_device/destination_dir_device" "${source_dir_device}/${destination_dir_device}" "debug"
|
|
if [[ "${source_dir_device}" == "${destination_dir_device}" ]]; then
|
|
# loop over source_files, display the size of each file, and move it
|
|
for source_file in "${source_files[@]}"; do
|
|
declare base_name_source="${source_file##*/}" source_size_human=""
|
|
source_size_human=$(stat -c %s "${source_file}" | numfmt --to=iec-i --suffix=B --format="%.2f")
|
|
display_alert "Fast-moving file to output/images" "-> ${base_name_source} (${source_size_human})" "info"
|
|
run_host_command_logged mv "${source_file}" "${destination_dir}"
|
|
done
|
|
else
|
|
display_alert "Moving artefacts using rsync to final destination" "${version}" "info"
|
|
run_host_command_logged rsync -av --no-owner --no-group --remove-source-files "${DESTIMG}/${version}"* "${FINALDEST}"
|
|
run_host_command_logged rm -rfv --one-file-system "${DESTIMG}"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function trap_handler_cleanup_destimg() {
|
|
[[ ! -d "${DESTIMG}" ]] && return 0
|
|
display_alert "Cleaning up temporary DESTIMG" "${DESTIMG}" "debug"
|
|
rm -rf --one-file-system "${DESTIMG}"
|
|
}
|