#!/usr/bin/env bash # ============================================================================= # vendor.sh - 제3자 의존성 벤더링 (NETWORKED 빌드 머신 전용) # # 폐쇄망(air-gapped) 타깃으로 전달하기 위해, 빌드/스테이징 머신에서 핀 고정된 # 제3자 의존성을 내려받아 SHA-256 으로 무결성을 검증한 뒤 firmware/third_party # 아래에 펼친다. **네트워크가 허용되는 유일한 단계**다. 이후 build.ps1/CMake 는 # 네트워크 없이 빌드한다. # # 사용: # firmware/scripts/vendor.sh # 표준 벤더링 # FORCE=1 firmware/scripts/vendor.sh # 기존 트리 덮어쓰기(재벤더링) # KEEP_ARCHIVES=1 firmware/scripts/vendor.sh # 압축본 보존(_archives) # # 요구 도구: curl(또는 wget), sha256sum(또는 shasum), unzip, tar. # # 중요: 아래 DEPS 의 URL/SHA256/TAG 는 운영에서 쓸 정확한 핀 버전으로 교체할 것. # SHA256 이 'TODO' 인 항목은 검증을 생략하고 계산된 해시를 출력만 한다 # (최초 1회 기록용). 기록 후 반드시 채워 넣어 재현성을 고정한다. # TODO(vendor): 핀 버전 확정. # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" FW_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" THIRD_PARTY="${FW_ROOT}/third_party" ARCHIVE_DIR="${THIRD_PARTY}/_archives" FORCE="${FORCE:-0}" KEEP_ARCHIVES="${KEEP_ARCHIVES:-0}" mkdir -p "${THIRD_PARTY}" [ "${KEEP_ARCHIVES}" = "1" ] && mkdir -p "${ARCHIVE_DIR}" # ── 핀 고정 의존성 (name|tag|url|sha256) ───────────────────────────────────── # name : third_party 하위 디렉터리명 (CMakeLists 경로와 일치) # tag : 릴리스/태그(추적용) # url : 다운로드 URL # sha256 : 압축 파일 SHA-256(소문자). 'TODO' 면 검증 생략(해시 출력만). # TODO(vendor): 아래 값들을 정확한 핀 버전으로 교체. DEPS=( "STM32CubeF4|v1.28.0|https://github.com/STMicroelectronics/STM32CubeF4/archive/refs/tags/v1.28.0.zip|TODO" "FreeRTOS-Kernel|V11.1.0|https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V11.1.0.zip|TODO" "lwip|STABLE-2_2_0_RELEASE|https://github.com/lwip-tcpip/lwip/archive/refs/tags/STABLE-2_2_0_RELEASE.zip|TODO" "mbedtls|v3.6.2|https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-3.6.2/mbedtls-3.6.2.tar.bz2|TODO" ) # ── 헬퍼: 다운로드 ─────────────────────────────────────────────────────────── download() { # url out local url="$1" out="$2" if command -v curl >/dev/null 2>&1; then curl -fL --proto '=https' --tlsv1.2 -o "${out}" "${url}" elif command -v wget >/dev/null 2>&1; then wget --https-only -O "${out}" "${url}" else echo "ERROR: curl 또는 wget 이 필요합니다." >&2; exit 1 fi } # ── 헬퍼: SHA-256 ──────────────────────────────────────────────────────────── sha256_of() { # path -> stdout(소문자 hex) local p="$1" if command -v sha256sum >/dev/null 2>&1; then sha256sum "${p}" | awk '{print tolower($1)}' elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "${p}" | awk '{print tolower($1)}' else echo "ERROR: sha256sum 또는 shasum 이 필요합니다." >&2; exit 1 fi } # ── 헬퍼: 압축 해제(zip / tar.*) ───────────────────────────────────────────── extract() { # archive dest local archive="$1" dest="$2" mkdir -p "${dest}" case "${archive}" in *.zip) command -v unzip >/dev/null 2>&1 || { echo "ERROR: unzip 필요" >&2; exit 1; } unzip -q "${archive}" -d "${dest}" ;; *.tar.bz2|*.tbz2|*.tar.gz|*.tgz|*.tar.xz) tar -xf "${archive}" -C "${dest}" ;; *) echo "ERROR: 지원하지 않는 압축 형식: ${archive}" >&2; exit 1 ;; esac } for entry in "${DEPS[@]}"; do IFS='|' read -r name tag url sha256 <<< "${entry}" target="${THIRD_PARTY}/${name}" echo "──────────────────────────────────────────────" echo " 벤더링: ${name} (${tag})" if [ -d "${target}" ] && [ "${FORCE}" != "1" ]; then echo " 이미 존재 — 건너뜀(재벤더링은 FORCE=1). ${target}" continue fi rm -rf "${target}" tmp="$(mktemp -d)" trap 'rm -rf "${tmp}"' EXIT fname="$(basename "${url%%\?*}")" archive="${tmp}/${fname}" echo " 다운로드: ${url}" download "${url}" "${archive}" # ── 무결성 검증 ──────────────────────────────────────────────────────────── actual="$(sha256_of "${archive}")" if [ "${sha256}" = "TODO" ] || [ -z "${sha256}" ]; then echo " [경고] SHA-256 미설정 — 검증 생략. 계산값을 기록하세요:" echo " ${name} : ${actual}" elif [ "${actual}" != "$(echo "${sha256}" | tr 'A-Z' 'a-z')" ]; then echo " ERROR: SHA-256 불일치(${name})" >&2 echo " 기대: ${sha256}" >&2 echo " 실제: ${actual}" >&2 exit 1 else echo " SHA-256 확인됨: ${actual}" fi # ── 해제 + 최상위 단일 폴더 벗기기 ──────────────────────────────────────── ex="${tmp}/x" extract "${archive}" "${ex}" # 최상위가 단일 디렉터리면 그 내용을 target 으로, 아니면 ex 자체를 target 으로. shopt -s nullglob tops=("${ex}"/*) shopt -u nullglob if [ "${#tops[@]}" -eq 1 ] && [ -d "${tops[0]}" ]; then mv "${tops[0]}" "${target}" else mv "${ex}" "${target}" fi if [ "${KEEP_ARCHIVES}" = "1" ]; then cp -f "${archive}" "${ARCHIVE_DIR}/${fname}" fi rm -rf "${tmp}" trap - EXIT echo " 완료 → ${target}" done echo "──────────────────────────────────────────────" echo " 벤더링 완료. 이제 폐쇄망 빌드 가능." echo " TODO(vendor): 위 출력된 SHA-256 값을 DEPS 에 채워 재현성을 고정하세요."