Files

165 lines
5.7 KiB
Bash
Raw Permalink Normal View History

2026-08-17 18:42:29 +00:00
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
readonly PROJECT_USERPATCHES="${SCRIPT_DIR}/userpatches"
readonly SOURCE_LOCK="${PROJECT_USERPATCHES}/source-lock.env"
readonly FRAMEWORK_OVERRIDES="${SCRIPT_DIR}/framework-overrides"
die() {
echo "build-image.sh: $*" >&2
exit 1
}
usage() {
cat <<'EOF'
Usage: build-image.sh --ssh-public-key PATH [OPTIONS]
Build the source-locked Armbian Debian Trixie image for Turing RK1.
Options:
--ssh-public-key PATH Required OpenSSH public-key/authorized_keys file.
--work-dir PATH Managed framework/cache directory (default: .work).
--prepare-only Clone/stage inputs, but do not invoke Armbian.
--config-dump Stage inputs and run Armbian's non-building config dump.
-h, --help Show this help.
The default action performs the full image build. It may ask for sudo through
Armbian's normal native-build launcher. No image is flashed by this command.
EOF
}
validate_public_keys() {
local key_file="${1}"
[[ -f "${key_file}" ]] || die "public-key file does not exist: ${key_file}"
[[ -s "${key_file}" ]] || die "public-key file is empty: ${key_file}"
command -v ssh-keygen >/dev/null || die "ssh-keygen is required to validate the public key"
awk '
/^[[:space:]]*($|#)/ { next }
$1 !~ /^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(256|384|521)|[email protected]|[email protected])$/ { exit 1 }
{ count++ }
END { if (count < 1) exit 1 }
' "${key_file}" || die "use plain OpenSSH public keys without authorized_keys options"
ssh-keygen -l -f "${key_file}" >/dev/null || die "ssh-keygen rejected the supplied public-key file"
}
prepare_framework() {
local work_root="${1}"
local framework_dir="${work_root}/armbian-build"
local origin
install -d -m 0755 "${work_root}"
work_root="$(cd -- "${work_root}" && pwd -P)"
framework_dir="${work_root}/armbian-build"
[[ "${work_root}" != "/" ]] || die "refusing to use / as the work directory"
if [[ ! -e "${framework_dir}" ]]; then
git init "${framework_dir}" >/dev/null
git -C "${framework_dir}" remote add origin "${RK1_MEDIA_ARMBIAN_BUILD_URL}"
install -m 0600 /dev/null "${framework_dir}/.rk1-media-build-root"
fi
[[ -d "${framework_dir}/.git" ]] || die "work path is not a Git checkout: ${framework_dir}"
[[ -f "${framework_dir}/.rk1-media-build-root" ]] || die "work checkout is not managed by this script: ${framework_dir}"
origin="$(git -C "${framework_dir}" remote get-url origin)"
[[ "${origin}" == "${RK1_MEDIA_ARMBIAN_BUILD_URL}" ]] || die "unexpected Armbian origin: ${origin}"
if git -C "${framework_dir}" rev-parse --verify HEAD >/dev/null 2>&1; then
git -C "${framework_dir}" diff --quiet || die "tracked modifications exist in ${framework_dir}"
git -C "${framework_dir}" diff --cached --quiet || die "staged modifications exist in ${framework_dir}"
fi
if ! git -C "${framework_dir}" cat-file -e "${RK1_MEDIA_ARMBIAN_BUILD_COMMIT}^{commit}" 2>/dev/null; then
git -C "${framework_dir}" fetch --depth=1 origin "${RK1_MEDIA_ARMBIAN_BUILD_COMMIT}"
fi
git -C "${framework_dir}" checkout --detach "${RK1_MEDIA_ARMBIAN_BUILD_COMMIT}" >/dev/null
printf '%s\n' "${framework_dir}"
}
stage_userpatches() {
local framework_dir="${1}"
local public_key="${2}"
local destination="${framework_dir}/userpatches"
[[ -f "${framework_dir}/.rk1-media-build-root" ]] || die "refusing to stage into an unmanaged checkout"
[[ "${destination}" == "${framework_dir}/userpatches" ]] || die "internal userpatch path check failed"
[[ ! -L "${destination}" ]] || die "refusing to replace a symlinked userpatches directory"
if [[ -e "${destination}" ]]; then
[[ -f "${destination}/.rk1-media-managed" ]] || die "existing userpatches are not managed by this script"
rm -rf -- "${destination}"
fi
install -d -m 0755 "${destination}"
cp -a "${PROJECT_USERPATCHES}/." "${destination}/"
install -m 0600 /dev/null "${destination}/.rk1-media-managed"
install -d -m 0700 "${destination}/overlay/build-input"
install -m 0600 "${public_key}" "${destination}/overlay/build-input/authorized_keys"
install -m 0644 "${SOURCE_LOCK}" "${destination}/overlay/build-input/source-lock.env"
# Armbian supports immutable resolutions for otherwise moving branch inputs
# through config/sources/git_sources.json. This pins its firmware artifact.
cp -a "${FRAMEWORK_OVERRIDES}/." "${framework_dir}/"
}
main() {
local public_key=""
local work_dir="${SCRIPT_DIR}/.work"
local mode="build"
local framework_dir
while (($#)); do
case "${1}" in
--ssh-public-key)
(($# >= 2)) || die "--ssh-public-key requires a path"
public_key="${2}"
shift 2
;;
--work-dir)
(($# >= 2)) || die "--work-dir requires a path"
work_dir="${2}"
shift 2
;;
--prepare-only)
mode="prepare"
shift
;;
--config-dump)
mode="config-dump"
shift
;;
-h|--help)
usage
exit 0
;;
*) die "unknown option: ${1}" ;;
esac
done
[[ -n "${public_key}" ]] || die "--ssh-public-key is required; the image never enables password login"
[[ -r "${SOURCE_LOCK}" ]] || die "source lock is missing: ${SOURCE_LOCK}"
# shellcheck source=userpatches/source-lock.env
source "${SOURCE_LOCK}"
validate_public_keys "${public_key}"
framework_dir="$(prepare_framework "${work_dir}")"
stage_userpatches "${framework_dir}" "${public_key}"
echo "Prepared Armbian ${RK1_MEDIA_ARMBIAN_BUILD_COMMIT} in ${framework_dir}"
case "${mode}" in
prepare)
echo "Configuration staged; no build was started."
;;
config-dump)
(cd "${framework_dir}" && CONFIG_DEFS_ONLY=yes ./compile.sh rk1-media config-dump)
;;
build)
(cd "${framework_dir}" && ./compile.sh rk1-media build)
echo "Images and checksums: ${framework_dir}/output/images"
;;
esac
}
main "$@"