|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | +IFS=$'\n\t' |
| 4 | + |
| 5 | +# ----------------------------------------------------------------------------- |
| 6 | +# Configuration |
| 7 | +# ----------------------------------------------------------------------------- |
| 8 | +SOURCE_OF_TRUTH="${SOURCE_OF_TRUTH:-rsync://fi.mirror.armbian.de}" |
| 9 | +OS_DIR="${OS_DIR:-./os}" |
| 10 | +BOARD_DIR="${BOARD_DIR:-./build/config/boards}" |
| 11 | +OUT="${OUT:-armbian-images.json}" |
| 12 | + |
| 13 | +# ----------------------------------------------------------------------------- |
| 14 | +# Requirements |
| 15 | +# ----------------------------------------------------------------------------- |
| 16 | +need() { command -v "$1" >/dev/null || { echo "ERROR: missing '$1'" >&2; exit 1; }; } |
| 17 | +need rsync gh jq jc find grep sed cut awk |
| 18 | + |
| 19 | +[[ -f "${OS_DIR}/exposed.map" ]] || { echo "ERROR: ${OS_DIR}/exposed.map not found" >&2; exit 1; } |
| 20 | +[[ -d "${BOARD_DIR}" ]] || { echo "ERROR: board directory not found: ${BOARD_DIR}" >&2; exit 1; } |
| 21 | + |
| 22 | +# ----------------------------------------------------------------------------- |
| 23 | +# Extract variable from board config |
| 24 | +# ----------------------------------------------------------------------------- |
| 25 | +extract_cfg_var() { |
| 26 | + local file="$1" var="$2" |
| 27 | + awk -v var="$var" ' |
| 28 | + { |
| 29 | + line=$0 |
| 30 | + sub(/[ \t]*#.*/, "", line) |
| 31 | + if (line ~ var"[ \t]*=") { |
| 32 | + sub(/^.*=/,"",line) |
| 33 | + gsub(/^["'\'']|["'\'']$/,"",line) |
| 34 | + print line; exit |
| 35 | + } |
| 36 | + }' "$file" 2>/dev/null || true |
| 37 | +} |
| 38 | + |
| 39 | +# ----------------------------------------------------------------------------- |
| 40 | +# Load board metadata |
| 41 | +# ----------------------------------------------------------------------------- |
| 42 | +declare -A BOARD_NAME_MAP BOARD_VENDOR_MAP |
| 43 | +while IFS= read -r cfg; do |
| 44 | + slug="$(basename "${cfg%.*}")" |
| 45 | + slug="${slug,,}" |
| 46 | + name="$(extract_cfg_var "$cfg" BOARD_NAME)" |
| 47 | + vendor="$(extract_cfg_var "$cfg" BOARD_VENDOR)" |
| 48 | + [[ -n "$name" ]] && BOARD_NAME_MAP["$slug"]="$name" |
| 49 | + [[ -n "$vendor" ]] && BOARD_VENDOR_MAP["$slug"]="$vendor" |
| 50 | +done < <(find "$BOARD_DIR" -maxdepth 1 -type f) |
| 51 | + |
| 52 | +# ----------------------------------------------------------------------------- |
| 53 | +# Helpers |
| 54 | +# ----------------------------------------------------------------------------- |
| 55 | +is_version_token() { [[ "$1" =~ ^[0-9]{2}\.[0-9] ]]; } |
| 56 | + |
| 57 | +is_preinstalled_app() { |
| 58 | + case "$1" in |
| 59 | + kali|homeassistant|openhab|omv) return 0 ;; |
| 60 | + *) return 1 ;; |
| 61 | + esac |
| 62 | +} |
| 63 | + |
| 64 | +strip_img_ext() { |
| 65 | + sed -E 's/(\.img(\.(xz|zst|gz))?)$//' <<<"$1" |
| 66 | +} |
| 67 | + |
| 68 | +extract_file_extension() { |
| 69 | + local n="$1" |
| 70 | + [[ "$n" == *.img.xz ]] && echo "img.xz" && return |
| 71 | + [[ "$n" == *.img.zst ]] && echo "img.zst" && return |
| 72 | + [[ "$n" == *.img.gz ]] && echo "img.gz" && return |
| 73 | + [[ "$n" == *.img ]] && echo "img" && return |
| 74 | + echo "${n##*.}" |
| 75 | +} |
| 76 | + |
| 77 | +get_download_repository() { |
| 78 | + local url="$1" |
| 79 | + if [[ "$url" == https://github.com/armbian/* ]]; then |
| 80 | + awk -F/ '{print $5}' <<<"$url" |
| 81 | + elif [[ "$url" == https://dl.armbian.com/* ]]; then |
| 82 | + awk -F/ '{print $5}' <<<"$url" |
| 83 | + else |
| 84 | + echo "" |
| 85 | + fi |
| 86 | +} |
| 87 | + |
| 88 | +# ----------------------------------------------------------------------------- |
| 89 | +# Parse image filename |
| 90 | +# ----------------------------------------------------------------------------- |
| 91 | +parse_image_name() { |
| 92 | + local name="$1" |
| 93 | + IFS="_" read -r -a p <<<"$name" |
| 94 | + |
| 95 | + local ver="" board="" distro="" branch="" kernel="" tail="" |
| 96 | + local variant="server" app="" storage="" |
| 97 | + |
| 98 | + if is_version_token "${p[1]:-}"; then |
| 99 | + ver="${p[1]}"; board="${p[2]}"; distro="${p[3]}"; branch="${p[4]}"; kernel="${p[5]}"; tail="${p[6]:-}" |
| 100 | + else |
| 101 | + ver="${p[2]}"; board="${p[3]}"; distro="${p[4]}"; branch="${p[5]}"; kernel="${p[6]}"; tail="${p[7]:-}" |
| 102 | + fi |
| 103 | + |
| 104 | + # kernel token may contain "-suffix" |
| 105 | + if [[ "$kernel" == *-* ]]; then |
| 106 | + suffix="$(strip_img_ext "${kernel#*-}")" |
| 107 | + if is_preinstalled_app "$suffix"; then |
| 108 | + app="$suffix" |
| 109 | + else |
| 110 | + [[ "${suffix##*-}" == "ufs" ]] && storage="ufs" |
| 111 | + fi |
| 112 | + fi |
| 113 | + |
| 114 | + [[ "$tail" == minimal* ]] && variant="minimal" |
| 115 | + [[ "$name" == *_desktop.img.* ]] && variant="$tail" |
| 116 | + |
| 117 | + printf '%s\n' "$ver" "$board" "$distro" "$branch" "$variant" "$app" "$storage" |
| 118 | +} |
| 119 | + |
| 120 | +# ----------------------------------------------------------------------------- |
| 121 | +# Build feeds (NO .txt files) |
| 122 | +# ----------------------------------------------------------------------------- |
| 123 | +tmpdir="$(mktemp -d)" |
| 124 | +trap 'rm -rf "$tmpdir"' EXIT |
| 125 | +feed="$tmpdir/feed.txt" |
| 126 | + |
| 127 | +echo "▶ Building feeds…" >&2 |
| 128 | + |
| 129 | +# Mirror feed |
| 130 | +rsync --recursive --list-only "${SOURCE_OF_TRUTH}/dl/" | |
| 131 | +awk ' |
| 132 | +{ |
| 133 | + size=$2; gsub(/[.,]/,"",size) |
| 134 | + url="https://dl.armbian.com/" $5 |
| 135 | + if (url ~ /\/[^\/]+\/archive\/Armbian/ && |
| 136 | + url !~ /\.txt$/ && |
| 137 | + url !~ /\.(asc|sha|torrent)$/ && |
| 138 | + url !~ /(homeassistant|openhab|kali|omv)/) { |
| 139 | + dt=$3 "T" $4 "Z"; gsub("/", "-", dt) |
| 140 | + print size "|" url "|" dt |
| 141 | + } |
| 142 | +}' >"$tmpdir/a.txt" |
| 143 | + |
| 144 | +# GitHub feed |
| 145 | +: >"$tmpdir/bcd.txt" |
| 146 | +for repo in community os distribution; do |
| 147 | + gh release view --json assets --repo "github.com/armbian/$repo" | |
| 148 | + jq -r '.assets[] |
| 149 | + | select(.url | test("\\.txt($|\\?)") | not) |
| 150 | + | select(.url | test("\\.(asc|sha|torrent)($|\\?)") | not) |
| 151 | + | "\(.size)|\(.url)|\(.createdAt)"' >>"$tmpdir/bcd.txt" |
| 152 | +done |
| 153 | + |
| 154 | +cat "$tmpdir/a.txt" "$tmpdir/bcd.txt" >"$feed" |
| 155 | + |
| 156 | +# ----------------------------------------------------------------------------- |
| 157 | +# JSON generation |
| 158 | +# ----------------------------------------------------------------------------- |
| 159 | +{ |
| 160 | + echo '"board_slug"|"board_name"|"board_vendor"|"armbian_version"|"file_url"|"file_url_asc"|"file_url_sha"|"file_url_torrent"|"redi_url"|"redi_url_asc"|"redi_url_sha"|"redi_url_torrent"|"file_updated"|"file_size"|"distro_release"|"kernel_branch"|"image_variant"|"preinstalled_application"|"promoted"|"download_repository"|"file_extension"' |
| 161 | + |
| 162 | + while IFS="|" read -r SIZE URL DATE; do |
| 163 | + IMAGE_SIZE="${SIZE//[.,]/}" |
| 164 | + IMAGE_NAME="${URL##*/}" |
| 165 | + |
| 166 | + mapfile -t p < <(parse_image_name "$IMAGE_NAME") |
| 167 | + VER="${p[0]}"; BOARD="${p[1]}"; DISTRO="${p[2]}"; BRANCH="${p[3]}" |
| 168 | + VARIANT="${p[4]}"; APP="${p[5]}"; STORAGE="${p[6]}" |
| 169 | + |
| 170 | + [[ -z "$BOARD" ]] && continue |
| 171 | + BOARD_SLUG="${BOARD,,}" |
| 172 | + |
| 173 | + REPO="$(get_download_repository "$URL")" |
| 174 | + [[ -z "$REPO" ]] && continue |
| 175 | + |
| 176 | + PREFIX=""; [[ "$REPO" == "os" ]] && PREFIX="nightly/" |
| 177 | + |
| 178 | + # -------- file_extension (FINAL RULES) -------- |
| 179 | + BASE_EXT="$(extract_file_extension "$IMAGE_NAME")" |
| 180 | + if [[ "$IMAGE_NAME" == *.oowow.img.xz ]]; then |
| 181 | + FILE_EXTENSION="oowow.img.xz" |
| 182 | + elif [[ -n "$STORAGE" ]]; then |
| 183 | + FILE_EXTENSION="${STORAGE}.${BASE_EXT}" |
| 184 | + else |
| 185 | + FILE_EXTENSION="$BASE_EXT" |
| 186 | + fi |
| 187 | + # --------------------------------------------- |
| 188 | + |
| 189 | + APP_SUFFIX=""; [[ -n "$APP" ]] && APP_SUFFIX="-${APP}" |
| 190 | + REDI_URL="https://dl.armbian.com/${PREFIX}${BOARD_SLUG}/${DISTRO^}_${BRANCH}_${VARIANT}${APP_SUFFIX}" |
| 191 | + |
| 192 | + if [[ "$URL" == https://github.com/armbian/* ]]; then |
| 193 | + CACHE="https://cache.armbian.com/artifacts/${BOARD_SLUG}/archive/${IMAGE_NAME}" |
| 194 | + ASC="$CACHE.asc"; SHA="$CACHE.sha"; TOR="$CACHE.torrent" |
| 195 | + else |
| 196 | + ASC="$URL.asc"; SHA="$URL.sha"; TOR="$URL.torrent" |
| 197 | + fi |
| 198 | + |
| 199 | + echo "${BOARD_SLUG}|${BOARD_NAME_MAP[$BOARD_SLUG]:-}|${BOARD_VENDOR_MAP[$BOARD_SLUG]:-}|${VER}|${URL}|${ASC}|${SHA}|${TOR}|${REDI_URL}|${REDI_URL}.asc|${REDI_URL}.sha|${REDI_URL}.torrent|${DATE}|${IMAGE_SIZE}|${DISTRO}|${BRANCH}|${VARIANT}|${APP}|false|${REPO}|${FILE_EXTENSION}" |
| 200 | + done <"$feed" |
| 201 | + |
| 202 | +} | jc --csv | jq '{assets:.}' >"$OUT" |
| 203 | + |
| 204 | +echo "✔ Generated $OUT" |
| 205 | +echo "✔ Assets: $(jq '.assets | length' "$OUT")" |
0 commit comments