mirror of
https://github.com/jetkvm/rv1106-system.git
synced 2026-05-21 05:20:41 +00:00
1eea5e6bbc
- flash_system.sh: rename ota_tar -> system_tar (path is staged release-artifacts/<sku>/system.tar, not output/image/update_ota.tar) and only re-check device reachability after a --build, not on every invocation. - common.sh: add require_arg and prompt for SKU via normalize_system_sku so aliases live in one place; align FLASH_SKU/SYSTEM_SKU precedence between build_system.sh and flash_system.sh. - check_device.sh (new): one wrapper around check_ping/check_ssh used by both make check_device and direct CLI, removing the Makefile's hardcoded ping -c 1 / root@ duplication. - release_r2.sh, release_github.sh, run_e2e_tests.sh, build_system.sh, flash_system.sh: route all value-taking options through require_arg for consistent missing-arg errors.
262 lines
8.2 KiB
Bash
Executable File
262 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -e
|
|
set -o pipefail
|
|
|
|
SCRIPT_DIR=$(realpath "$(dirname "$(realpath "${BASH_SOURCE[0]}")")")
|
|
ROOT_DIR=$(realpath "${SCRIPT_DIR}/..")
|
|
|
|
BUILD_VERSION=""
|
|
R2_PATH="${R2_PATH:-r2://jetkvm-update/system}"
|
|
SIGNING_KEY_FPR=""
|
|
UNSIGNED=false
|
|
DRY_RUN=false
|
|
OTA_ROOT_KEY_FPR="AF5A36A993D828FEFE7C18C2D1B9856C26A79E95"
|
|
|
|
source "${SCRIPT_DIR}/common.sh"
|
|
|
|
show_help() {
|
|
echo "Usage: $0 --version <version> (--signing-key <fingerprint> | --unsigned) [--dry-run]"
|
|
echo
|
|
echo "Options:"
|
|
echo " --version <version> Release version (e.g., 0.2.7)"
|
|
echo " --signing-key <fpr> Sign the OTA payload with the trusted production key"
|
|
echo " --unsigned Upload without an OTA signature (dev/prerelease only)"
|
|
echo " --dry-run Sign and validate, but do not upload to R2"
|
|
echo " --help Show this help message"
|
|
echo
|
|
}
|
|
|
|
validate_signing_key() {
|
|
local signing_key_fpr="$1"
|
|
local root_fpr
|
|
|
|
if [ -z "$signing_key_fpr" ]; then
|
|
msg_err "Error: signing key fingerprint is required"
|
|
exit 1
|
|
fi
|
|
|
|
command -v gpg >/dev/null 2>&1 || { msg_err "Error: gpg not installed"; exit 1; }
|
|
gpg --list-secret-keys --with-colons "$signing_key_fpr" >/dev/null 2>&1 || {
|
|
msg_err "Error: Signing key ${signing_key_fpr} not found in local GPG keyring"
|
|
exit 1
|
|
}
|
|
|
|
root_fpr=$(gpg --list-secret-keys --with-colons "$signing_key_fpr" | awk -F: '/^fpr:/ { print $10; exit }')
|
|
if [ -z "$root_fpr" ]; then
|
|
msg_err "Error: Could not determine root fingerprint for signing key ${signing_key_fpr}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$root_fpr" != "$OTA_ROOT_KEY_FPR" ]; then
|
|
msg_err "Error: Signing key ${signing_key_fpr} belongs to root ${root_fpr}, expected ${OTA_ROOT_KEY_FPR}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--version)
|
|
require_arg "$1" "${2:-}"
|
|
BUILD_VERSION="$2"
|
|
shift 2
|
|
;;
|
|
--signing-key)
|
|
require_arg "$1" "${2:-}"
|
|
SIGNING_KEY_FPR="$2"
|
|
shift 2
|
|
;;
|
|
--unsigned)
|
|
UNSIGNED=true
|
|
shift
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
msg_err "Unknown option: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$BUILD_VERSION" ]; then
|
|
msg_err "Error: --version is required"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$SIGNING_KEY_FPR" ] && [ "$UNSIGNED" = true ]; then
|
|
msg_err "Error: choose either --signing-key or --unsigned, not both"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$SIGNING_KEY_FPR" ] && [ "$UNSIGNED" != true ]; then
|
|
msg_err "Error: choose --signing-key <fingerprint> for production or --unsigned for dev/prerelease uploads"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$SIGNING_KEY_FPR" ]; then
|
|
validate_signing_key "$SIGNING_KEY_FPR"
|
|
fi
|
|
|
|
if [ "$DRY_RUN" != true ]; then
|
|
command -v rclone >/dev/null 2>&1 || { msg_err "Error: rclone not installed"; exit 1; }
|
|
fi
|
|
|
|
cd "$ROOT_DIR"
|
|
|
|
validate_system_variant() {
|
|
local label="$1"
|
|
local sku="$2"
|
|
local stage_dir
|
|
local recovery_name
|
|
|
|
stage_dir=$(system_variant_dir "$sku")
|
|
recovery_name=$(recovery_artifact_for_sku "$sku")
|
|
for file in \
|
|
"${stage_dir}/${SYSTEM_TAR_NAME}" \
|
|
"${stage_dir}/${SYSTEM_TAR_NAME}.sha256" \
|
|
"${stage_dir}/${recovery_name}" \
|
|
"${stage_dir}/${recovery_name}.sha256"
|
|
do
|
|
if [ ! -f "$file" ]; then
|
|
msg_err "Error: Required ${label} artifact not found: $file"
|
|
msg_err "Run 'make build' first."
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
sign_system_variant() {
|
|
local label="$1"
|
|
local sku="$2"
|
|
local stage_dir
|
|
local system_tar
|
|
local system_sig
|
|
|
|
stage_dir=$(system_variant_dir "$sku")
|
|
system_tar="${stage_dir}/${SYSTEM_TAR_NAME}"
|
|
system_sig="${system_tar}.sig"
|
|
rm -f "$system_sig"
|
|
|
|
msg_info " Signing ${label} OTA payload (${sku})..."
|
|
for attempt in 1 2 3; do
|
|
if gpg --yes --detach-sign --output "$system_sig" --local-user "$SIGNING_KEY_FPR" "$system_tar"; then
|
|
break
|
|
fi
|
|
rm -f "$system_sig"
|
|
if [ "$attempt" -eq 3 ]; then
|
|
msg_err "Error: GPG signing failed after 3 attempts for ${label}"
|
|
exit 1
|
|
fi
|
|
msg_warn "GPG signing failed for ${label} (attempt ${attempt}/3). Please retry."
|
|
done
|
|
|
|
if [ ! -f "$system_sig" ]; then
|
|
msg_err "Error: Signature file not created: $system_sig"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
print_system_variant() {
|
|
local label="$1"
|
|
local sku="$2"
|
|
local stage_dir
|
|
local recovery_name
|
|
local system_hash
|
|
local recovery_hash
|
|
|
|
stage_dir=$(system_variant_dir "$sku")
|
|
recovery_name=$(recovery_artifact_for_sku "$sku")
|
|
system_hash=$(< "${stage_dir}/${SYSTEM_TAR_NAME}.sha256")
|
|
recovery_hash=$(< "${stage_dir}/${recovery_name}.sha256")
|
|
|
|
msg_info " - ${label} (${sku})"
|
|
msg_info " ${SYSTEM_TAR_NAME} → skus/${sku}/${SYSTEM_TAR_NAME}"
|
|
msg_info " SHA256: ${system_hash}"
|
|
if [ -n "$SIGNING_KEY_FPR" ]; then
|
|
msg_info " Signature: ${stage_dir}/${SYSTEM_TAR_NAME}.sig → skus/${sku}/${SYSTEM_TAR_NAME}.sig"
|
|
fi
|
|
msg_info " ${recovery_name} → skus/${sku}/${recovery_name}"
|
|
msg_info " SHA256: ${recovery_hash}"
|
|
}
|
|
|
|
upload_system_variant() {
|
|
local sku="$1"
|
|
local stage_dir
|
|
local recovery_name
|
|
local dest
|
|
|
|
stage_dir=$(system_variant_dir "$sku")
|
|
recovery_name=$(recovery_artifact_for_sku "$sku")
|
|
dest="${R2_PATH}/${BUILD_VERSION}/skus/${sku}"
|
|
|
|
rclone copyto --progress "${stage_dir}/${SYSTEM_TAR_NAME}" "${dest}/${SYSTEM_TAR_NAME}"
|
|
rclone copyto --progress "${stage_dir}/${SYSTEM_TAR_NAME}.sha256" "${dest}/${SYSTEM_TAR_NAME}.sha256"
|
|
if [ -n "$SIGNING_KEY_FPR" ]; then
|
|
rclone copyto --progress "${stage_dir}/${SYSTEM_TAR_NAME}.sig" "${dest}/${SYSTEM_TAR_NAME}.sig"
|
|
fi
|
|
rclone copyto --progress "${stage_dir}/${recovery_name}" "${dest}/${recovery_name}"
|
|
rclone copyto --progress "${stage_dir}/${recovery_name}.sha256" "${dest}/${recovery_name}.sha256"
|
|
}
|
|
|
|
validate_system_variant "SDMMC" "$SDMMC_SKU"
|
|
validate_system_variant "EMMC" "$EMMC_SKU"
|
|
|
|
if [ "$DRY_RUN" != true ]; then
|
|
if rclone lsf "${R2_PATH}/${BUILD_VERSION}/" 2>/dev/null | grep -q .; then
|
|
msg_err "Error: Version ${BUILD_VERSION} already exists in R2"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$SIGNING_KEY_FPR" ]; then
|
|
msg_info ">> Signing OTA payloads with ${SIGNING_KEY_FPR}..."
|
|
read -p "Ensure the YubiKey is inserted and ready, then continue signing? [y/N] " confirm_sign
|
|
if [ "$confirm_sign" != "y" ]; then
|
|
msg_warn "Signing cancelled."
|
|
exit 1
|
|
fi
|
|
|
|
sign_system_variant "SDMMC" "$SDMMC_SKU"
|
|
sign_system_variant "EMMC" "$EMMC_SKU"
|
|
fi
|
|
|
|
echo ""
|
|
msg_info "═══════════════════════════════════════════════════════"
|
|
if [ "$DRY_RUN" = true ]; then
|
|
msg_info " R2 Upload Dry Run"
|
|
else
|
|
msg_info " R2 Upload"
|
|
fi
|
|
msg_info "═══════════════════════════════════════════════════════"
|
|
msg_info " Destination: ${R2_PATH}/${BUILD_VERSION}/skus/<sku>/"
|
|
msg_info "═══════════════════════════════════════════════════════"
|
|
msg_info " Files to upload:"
|
|
print_system_variant "SDMMC" "$SDMMC_SKU"
|
|
print_system_variant "EMMC" "$EMMC_SKU"
|
|
msg_info "═══════════════════════════════════════════════════════"
|
|
echo ""
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
msg_ok "OK: R2 dry run complete; skipped upload"
|
|
exit 0
|
|
fi
|
|
|
|
read -p "The R2 upload is prepared. These are the files. Do you want to continue? [y/N] " confirm
|
|
if [ "$confirm" != "y" ]; then
|
|
msg_warn "R2 upload cancelled."
|
|
exit 1
|
|
fi
|
|
|
|
msg_info ">> Uploading to R2..."
|
|
upload_system_variant "$SDMMC_SKU"
|
|
upload_system_variant "$EMMC_SKU"
|
|
|
|
msg_ok "OK: Uploaded to R2: ${R2_PATH}/${BUILD_VERSION}/skus/"
|