ecosystem.sh (5303B)
1 #!/usr/bin/env bash 2 # scripts/ecosystem.sh — provision real-world C projects for the ecosystem gate. 3 # 4 # Fetches pinned, hash-verified upstream releases into the kit cache dir and 5 # extracts them, so the integration tests (test/ecosystem/run.sh) run fully 6 # offline against a reproducible source tree. The per-project metadata (URL, 7 # version, sha256, layout) is the single source of truth in 8 # test/ecosystem/recipes/<name>.recipe — this script just drives it. 9 # 10 # Cache dir resolution (matches driver/env's cache_dir convention): 11 # $KIT_ECO_CACHE (explicit override), else 12 # $XDG_CACHE_HOME/kit/ecosystem, else 13 # $HOME/.cache/kit/ecosystem, else 14 # <repo>/build/kit-cache/ecosystem 15 # 16 # Commands: 17 # list print recipe names, one per line 18 # cachedir print the resolved cache dir 19 # status show provisioned / missing per project 20 # entrydir <name> print the cache entry dir for <name> 21 # srcdir <name> print the source root (entry/<ECO_SRCDIR>); 22 # empty + exit 1 if not provisioned 23 # fetch <name>|all download + verify sha256 + extract 24 # clean <name>|all remove cache entries 25 # 26 # env: KIT_ECO_CACHE (cache override). No network except `fetch`. 27 28 set -eu 29 30 ROOT="$(cd "$(dirname "$0")/.." && pwd)" 31 RECIPE_DIR="$ROOT/test/ecosystem/recipes" 32 33 eco_cachedir() { 34 if [ -n "${KIT_ECO_CACHE:-}" ]; then printf '%s\n' "$KIT_ECO_CACHE"; return; fi 35 if [ -n "${XDG_CACHE_HOME:-}" ]; then printf '%s/kit/ecosystem\n' "$XDG_CACHE_HOME"; return; fi 36 if [ -n "${HOME:-}" ]; then printf '%s/.cache/kit/ecosystem\n' "$HOME"; return; fi 37 printf '%s/build/kit-cache/ecosystem\n' "$ROOT" 38 } 39 CACHE="$(eco_cachedir)" 40 41 die() { printf 'ecosystem: %s\n' "$*" >&2; exit 1; } 42 43 recipes() { 44 for r in "$RECIPE_DIR"/*.recipe; do 45 [ -e "$r" ] || continue 46 basename "$r" .recipe 47 done 48 } 49 50 recipe_path() { 51 local p="$RECIPE_DIR/$1.recipe" 52 [ -f "$p" ] || die "unknown project '$1' (see: $0 list)" 53 printf '%s\n' "$p" 54 } 55 56 # Load a recipe's provisioning vars into the current shell. (Functions are 57 # defined too but unused here.) Sets ECO_VERSION/URL/SHA256/SRCDIR. 58 load_recipe() { 59 ECO_VERSION=; ECO_URL=; ECO_SHA256=; ECO_SRCDIR=. 60 # shellcheck disable=SC1090 61 . "$(recipe_path "$1")" 62 [ -n "$ECO_URL" ] || die "$1: ECO_URL unset" 63 [ -n "$ECO_SHA256" ] || die "$1: ECO_SHA256 unset" 64 } 65 66 entrydir() { load_recipe "$1"; printf '%s/%s-%s\n' "$CACHE" "$1" "$ECO_VERSION"; } 67 68 srcdir() { 69 local e; e="$(entrydir "$1")" 70 load_recipe "$1" 71 local s="$e/$ECO_SRCDIR" 72 if [ -d "$s" ]; then printf '%s\n' "$s"; else return 1; fi 73 } 74 75 sha256_of() { 76 if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 77 elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 78 else die "no sha256sum/shasum available"; fi 79 } 80 81 fetch_one() { 82 local name="$1" 83 load_recipe "$name" 84 local entry; entry="$(entrydir "$name")" 85 if [ -d "$entry/$ECO_SRCDIR" ]; then 86 printf ' %-10s already provisioned (%s)\n' "$name" "$ECO_VERSION" 87 return 0 88 fi 89 command -v curl >/dev/null 2>&1 || die "curl required for fetch" 90 91 local tmp; tmp="$(mktemp -d "${TMPDIR:-/tmp}/kit-eco-$name.XXXXXX")" 92 local ar="$tmp/archive" 93 94 printf ' %-10s fetching %s\n' "$name" "$ECO_URL" 95 curl -fsSL --retry 3 -o "$ar" "$ECO_URL" || die "$name: download failed" 96 97 local got; got="$(sha256_of "$ar")" 98 if [ "$got" != "$ECO_SHA256" ]; then 99 die "$name: sha256 mismatch 100 expected $ECO_SHA256 101 got $got" 102 fi 103 104 rm -rf "$entry"; mkdir -p "$entry" 105 case "$ECO_URL" in 106 *.zip) 107 command -v unzip >/dev/null 2>&1 || die "unzip required for $name" 108 unzip -q -o "$ar" -d "$entry" ;; 109 *.tar.gz|*.tgz) 110 tar xzf "$ar" -C "$entry" ;; 111 *) die "$name: unknown archive type for $ECO_URL" ;; 112 esac 113 114 rm -rf "$tmp" 115 [ -d "$entry/$ECO_SRCDIR" ] || die "$name: ECO_SRCDIR '$ECO_SRCDIR' not found after extract" 116 printf ' %-10s OK %s -> %s\n' "$name" "$ECO_VERSION" "$entry/$ECO_SRCDIR" 117 } 118 119 cmd_fetch() { 120 mkdir -p "$CACHE" 121 if [ "${1:-all}" = all ]; then 122 for n in $(recipes); do fetch_one "$n"; done 123 else 124 fetch_one "$1" 125 fi 126 } 127 128 cmd_clean() { 129 if [ "${1:-all}" = all ]; then 130 for n in $(recipes); do rm -rf "$(entrydir "$n")"; done 131 printf 'cleaned all ecosystem cache entries under %s\n' "$CACHE" 132 else 133 rm -rf "$(entrydir "$1")"; printf 'cleaned %s\n' "$1" 134 fi 135 } 136 137 cmd_status() { 138 printf 'cache: %s\n' "$CACHE" 139 for n in $(recipes); do 140 if srcdir "$n" >/dev/null 2>&1; then 141 printf ' [x] %-10s %s\n' "$n" "$ECO_VERSION" 142 else 143 printf ' [ ] %-10s %s (run: %s fetch %s)\n' "$n" "$ECO_VERSION" "$0" "$n" 144 fi 145 done 146 } 147 148 main() { 149 local cmd="${1:-status}"; shift || true 150 case "$cmd" in 151 list|recipes) recipes ;; 152 cachedir) printf '%s\n' "$CACHE" ;; 153 status) cmd_status ;; 154 entrydir) [ $# -ge 1 ] || die "entrydir <name>"; entrydir "$1" ;; 155 srcdir) [ $# -ge 1 ] || die "srcdir <name>"; srcdir "$1" || { printf '' ; exit 1; } ;; 156 fetch) cmd_fetch "${1:-all}" ;; 157 clean) cmd_clean "${1:-all}" ;; 158 *) die "unknown command '$cmd' (list|cachedir|status|entrydir|srcdir|fetch|clean)" ;; 159 esac 160 } 161 162 main "$@"