#!/usr/bin/env bash set -Eeuo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" # shellcheck source=lib.sh . "${SCRIPT_DIR}/lib.sh" LOCK_FILE="${RKMEDIA_ROOT}/sources.lock.json" DEST_DIR="${RKMEDIA_ROOT}/out/sources" usage() { cat <<'EOF' Usage: fetch-sources.sh [--lock FILE] [--dest DIRECTORY] Fetch every build input at the exact commit recorded in the source lock. Existing repositories are accepted only when their origin and HEAD match. EOF } while (($#)); do case "$1" in --lock) [[ $# -ge 2 ]] || die "--lock requires a file" LOCK_FILE="$2" shift 2 ;; --dest) [[ $# -ge 2 ]] || die "--dest requires a directory" DEST_DIR="$2" shift 2 ;; -h|--help) usage exit 0 ;; *) die "unknown argument: $1" ;; esac done need_command git need_command jq LOCK_FILE="$(absolute_path "${LOCK_FILE}")" DEST_DIR="$(absolute_path "${DEST_DIR}")" [[ -f "${LOCK_FILE}" ]] || die "source lock not found: ${LOCK_FILE}" jq -e '.schema_version == 1 and (.sources | type == "array")' "${LOCK_FILE}" >/dev/null \ || die "invalid source lock: ${LOCK_FILE}" mkdir -p -- "${DEST_DIR}" verify_checkout() { local dir="$1" expected_repo="$2" expected_commit="$3" local actual_repo actual_commit [[ -d "${dir}/.git" ]] || return 1 actual_repo="$(git -C "${dir}" remote get-url origin 2>/dev/null || true)" actual_commit="$(git -C "${dir}" rev-parse HEAD 2>/dev/null || true)" [[ "${actual_repo}" == "${expected_repo}" && "${actual_commit}" == "${expected_commit}" ]] } while IFS=$'\t' read -r name repository ref commit; do [[ "${name}" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "unsafe source name in lock: ${name}" [[ "${commit}" =~ ^[0-9a-f]{40}$ ]] || die "invalid commit for ${name}: ${commit}" source_dir="${DEST_DIR}/${name}" if verify_checkout "${source_dir}" "${repository}" "${commit}"; then log "source already verified: ${name} ${commit}" continue fi if [[ -e "${source_dir}" ]]; then die "${source_dir} exists but does not match the lock; move it aside and retry" fi log "fetching ${name} at ${commit}" mkdir -p -- "${source_dir}" git -C "${source_dir}" init --quiet git -C "${source_dir}" remote add origin "${repository}" if ! git -C "${source_dir}" fetch --quiet --depth=1 origin "${commit}"; then log "direct commit fetch was unavailable; fetching history from ${ref}" git -C "${source_dir}" fetch --quiet --filter=blob:none origin "${ref}" fi git -C "${source_dir}" checkout --quiet --detach "${commit}" verify_checkout "${source_dir}" "${repository}" "${commit}" \ || die "checkout verification failed for ${name}" done < <(jq -r '.sources[] | select(.build == true) | [.name, .repository, .ref, .commit] | @tsv' "${LOCK_FILE}") log "all locked sources are present in ${DEST_DIR}"