Skip to content

Commit f6f8a27

Browse files
committed
Add armbian-images.json generator and workflow
1 parent 5e0d446 commit f6f8a27

File tree

2 files changed

+330
-0
lines changed

2 files changed

+330
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Update download JSON index
2+
3+
on:
4+
push:
5+
repository_dispatch:
6+
types: ["Webindex update"]
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
12+
concurrency:
13+
group: redirector
14+
cancel-in-progress: false
15+
16+
env:
17+
SOURCE_OF_TRUTH: "rsync://fi.mirror.armbian.de"
18+
19+
jobs:
20+
Webindex:
21+
name: "Generate JSON Index"
22+
runs-on: ubuntu-24.04
23+
24+
env:
25+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26+
27+
steps:
28+
- name: Checkout build framework repository
29+
uses: actions/checkout@v6
30+
with:
31+
repository: armbian/build
32+
fetch-depth: 1
33+
clean: false
34+
path: build
35+
36+
- name: Checkout OS repository
37+
uses: actions/checkout@v6
38+
with:
39+
repository: armbian/os
40+
fetch-depth: 1
41+
clean: false
42+
path: os
43+
44+
- name: Checkout armbian.github.io repository
45+
uses: actions/checkout@v6
46+
with:
47+
repository: armbian/armbian.github.io
48+
fetch-depth: 0
49+
clean: false
50+
path: armbian.github.io
51+
52+
- name: Checkout this repo (script)
53+
uses: actions/checkout@v6
54+
with:
55+
fetch-depth: 1
56+
clean: false
57+
path: runner
58+
59+
- name: Install dependencies
60+
run: |
61+
sudo apt-get update
62+
sudo apt-get install -y jq jc rsync
63+
64+
- name: Generate armbian-images.json
65+
working-directory: runner
66+
run: |
67+
set -euo pipefail
68+
OUT="armbian-images.json" \
69+
OS_DIR="${GITHUB_WORKSPACE}/os" \
70+
BOARD_DIR="${GITHUB_WORKSPACE}/build/config/boards" \
71+
SOURCE_OF_TRUTH="${SOURCE_OF_TRUTH}" \
72+
./scripts/generate-armbian-images-json.sh
73+
74+
# Copy into workspace root so later steps can reference easily
75+
cp armbian-images.json "${GITHUB_WORKSPACE}/armbian-images.json"
76+
77+
- name: Commit changes if any
78+
run: |
79+
set -euo pipefail
80+
cd armbian.github.io
81+
git checkout data
82+
mkdir -p data
83+
84+
cp "${GITHUB_WORKSPACE}/armbian-images.json" data/
85+
86+
git config user.name "github-actions"
87+
git config user.email "github-actions@github.com"
88+
89+
git add data/armbian-images.json
90+
git diff --cached --quiet || git commit -m "Update armbian-images.json"
91+
git push
92+
93+
- name: Run Jira update action
94+
uses: peter-evans/repository-dispatch@v4
95+
with:
96+
token: ${{ secrets.GITHUB_TOKEN }}
97+
event-type: "Jira update"
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
5+
# -----------------------------------------------------------------------------
6+
# Configuration (override via env)
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 rev 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+
# Helpers: parse first assignment of VAR=... from a board config file.
24+
# Supports:
25+
# BOARD_NAME="X"
26+
# export BOARD_NAME="X"
27+
# declare -g BOARD_NAME="X"
28+
# declare -g -r BOARD_NAME='\''X'\''
29+
# BOARD_VENDOR=generic
30+
# -----------------------------------------------------------------------------
31+
extract_cfg_var() {
32+
local file="$1" var="$2"
33+
34+
awk -v var="$var" '
35+
function ltrim(s){ sub(/^[ \t\r\n]+/, "", s); return s }
36+
function rtrim(s){ sub(/[ \t\r\n]+$/, "", s); return s }
37+
function trim(s){ return rtrim(ltrim(s)) }
38+
39+
{
40+
line=$0
41+
sub(/[ \t]*#.*/, "", line)
42+
line=trim(line)
43+
if (line == "") next
44+
45+
if (line ~ /^export[ \t]+/) {
46+
sub(/^export[ \t]+/, "", line)
47+
} else if (line ~ /^declare[ \t]+-/) {
48+
sub(/^declare[ \t]+(-[A-Za-z]+[ \t]+)+/, "", line)
49+
} else if (line ~ /^declare[ \t]+/) {
50+
sub(/^declare[ \t]+/, "", line)
51+
}
52+
53+
pattern="^" var "[ \t]*="
54+
if (line ~ pattern) {
55+
sub(pattern, "", line)
56+
line=trim(line)
57+
58+
if (match(line, /^".*"$/)) {
59+
sub(/^"/, "", line); sub(/"$/, "", line)
60+
} else if (match(line, /^\047.*\047$/)) {
61+
sub(/^\047/, "", line); sub(/\047$/, "", line)
62+
}
63+
64+
print line
65+
exit 0
66+
}
67+
}
68+
' "$file" 2>/dev/null || true
69+
}
70+
71+
# -----------------------------------------------------------------------------
72+
# Load board metadata
73+
# -----------------------------------------------------------------------------
74+
declare -A BOARD_NAME_MAP
75+
declare -A BOARD_VENDOR_MAP
76+
77+
while IFS= read -r cfg; do
78+
slug="$(basename "$cfg")"
79+
slug="${slug%.*}"
80+
slug="${slug,,}"
81+
82+
name="$(extract_cfg_var "$cfg" "BOARD_NAME")"
83+
vendor="$(extract_cfg_var "$cfg" "BOARD_VENDOR")"
84+
85+
[[ -n "$name" ]] && BOARD_NAME_MAP["$slug"]="$name"
86+
[[ -n "$vendor" ]] && BOARD_VENDOR_MAP["$slug"]="$vendor"
87+
done < <(
88+
find "$BOARD_DIR" -maxdepth 1 -type f \
89+
\( -name "*.conf" -o -name "*.csc" -o -name "*.wip" -o -name "*.tvb" \) | sort
90+
)
91+
92+
# -----------------------------------------------------------------------------
93+
# Warn once per board if metadata is missing
94+
# -----------------------------------------------------------------------------
95+
declare -A BOARD_WARNED
96+
97+
warn_missing_board_meta() {
98+
local slug="$1" name="$2" vendor="$3"
99+
100+
[[ -n "${BOARD_WARNED[$slug]:-}" ]] && return 0
101+
BOARD_WARNED["$slug"]=1
102+
103+
if [[ -z "$name" || -z "$vendor" ]]; then
104+
echo "WARNING: Missing board metadata for slug '${slug}' (name='${name}', vendor='${vendor}')" >&2
105+
fi
106+
}
107+
108+
# -----------------------------------------------------------------------------
109+
# Build feeds (drop .asc/.sha/.torrent as standalone entries)
110+
# -----------------------------------------------------------------------------
111+
tmpdir="$(mktemp -d)"
112+
trap 'rm -rf "$tmpdir"' EXIT
113+
feed="${tmpdir}/feed.txt"
114+
115+
echo "▶ Building feeds…" >&2
116+
117+
# Feed A: mirror
118+
rsync --recursive --list-only "${SOURCE_OF_TRUTH}/dl/" \
119+
| awk '{
120+
gsub(",", "", $2)
121+
gsub("/", "-", $3)
122+
url = "https://dl.armbian.com/" $5
123+
if (url ~ /\/[^\/]+\/archive\/Armbian/ &&
124+
url !~ /\.txt|homeassistant|openhab|kali|omv/ &&
125+
url !~ /\.(asc|sha|torrent)$/) {
126+
print $2 "|" url "|" $3 "T" $4 "Z"
127+
}
128+
}' > "${tmpdir}/a.txt"
129+
130+
# Feed B/C/D: GitHub releases
131+
: > "${tmpdir}/bcd.txt"
132+
for repo in community os distribution; do
133+
gh release view --json assets --repo "github.com/armbian/${repo}" \
134+
| jq -r '.assets[]
135+
| select(.url | test("\\.(asc|sha|torrent)$") | not)
136+
| select(.url | test("\\.txt$") | not)
137+
| "\(.size)|\(.url)|\(.createdAt)"' \
138+
>> "${tmpdir}/bcd.txt"
139+
done
140+
141+
cat "${tmpdir}/a.txt" "${tmpdir}/bcd.txt" > "$feed"
142+
143+
# -----------------------------------------------------------------------------
144+
# JSON generation
145+
# -----------------------------------------------------------------------------
146+
{
147+
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"'
148+
149+
while IFS= read -r line; do
150+
[[ -z "$line" ]] && continue
151+
152+
IMAGE_URL="$(cut -d"|" -f2 <<<"$line")"
153+
IMAGE_SIZE="$(cut -d"|" -f1 <<<"$line" | sed 's/[.,]//g')"
154+
IMAGE_CREATED="$(cut -d"|" -f3 <<<"$line" | sed 's/\//-/g')"
155+
156+
BOARD="$(echo "$IMAGE_URL" | grep -Po 'Armbian.*[0-9][0-9]\.[0-9].*' | grep -Po '[0-9][0-9]\.[0-9].*' | cut -d_ -f2)"
157+
ARMBIAN_VERSION="$(echo "$IMAGE_URL" | grep -Po 'Armbian.*[0-9][0-9]\.[0-9].*' | grep -Po '[0-9][0-9]\.[0-9].*' | cut -d_ -f1)"
158+
IMAGE_RELEASE="$(echo "$IMAGE_URL" | grep -Po 'Armbian.*[0-9][0-9]\.[0-9].*' | grep -Po '[0-9][0-9]\.[0-9].*' | cut -d_ -f3)"
159+
KERNEL_BRANCH="$(echo "$IMAGE_URL" | grep -Po 'Armbian.*[0-9][0-9]\.[0-9].*' | grep -Po '[0-9][0-9]\.[0-9].*' | cut -d_ -f4)"
160+
161+
BOARD_SLUG="${BOARD,,}"
162+
BOARD_NAME="${BOARD_NAME_MAP[$BOARD_SLUG]:-}"
163+
BOARD_VENDOR="${BOARD_VENDOR_MAP[$BOARD_SLUG]:-}"
164+
165+
warn_missing_board_meta "$BOARD_SLUG" "$BOARD_NAME" "$BOARD_VENDOR"
166+
167+
STEP_A=1
168+
STEP_B=2
169+
[[ $IMAGE_URL == *i3-wm* || $IMAGE_URL == *kde-* || $IMAGE_URL == *-rc* ]] && STEP_A=2 && STEP_B=3
170+
171+
IMAGE_VARIANT="$(
172+
echo "$IMAGE_URL" \
173+
| grep "minimal\|desktop" \
174+
| grep -Po '[0-9][0-9]\.[0-9].*' \
175+
| cut -d_ -f4- \
176+
| grep -Po '_[a-z].*' \
177+
| cut -d. -f1 \
178+
| sed 's/^_//; s/_desktop//; s/-kisak//; s/-backported-mesa//' \
179+
| cut -d- -f1,${STEP_A} || true
180+
)"
181+
[[ -z "$IMAGE_VARIANT" ]] && IMAGE_VARIANT="server"
182+
183+
FILE_EXTENSION="$(
184+
echo "$IMAGE_URL" \
185+
| grep -Po 'Armbian.*[0-9][0-9]\.[0-9].*' \
186+
| grep -Po '[0-9][0-9]\.[0-9].*' \
187+
| sed 's/_sm8250-xiaomi-umi\|_sm8250-xiaomi-elish//g' \
188+
| sed 's/-rc[0-9]//g' \
189+
| rev | cut -d_ -f1 | rev \
190+
| sed 's/.*[^0-9]\([0-9]*\.[0-9]*\.[0-9]*\)//' \
191+
| sed 's/desktop.\|minimal.//'
192+
)"
193+
194+
IMAGE_TYPE="$(cut -d/ -f5 <<<"$IMAGE_URL")"
195+
PREFIX=""
196+
[[ "$IMAGE_TYPE" == "os" ]] && PREFIX="nightly/"
197+
198+
REDI_URL="https://dl.armbian.com/${PREFIX}${BOARD_SLUG}/${IMAGE_RELEASE^}_${KERNEL_BRANCH}_${IMAGE_VARIANT}"
199+
200+
IMAGE_NAME="${IMAGE_URL##*/}"
201+
202+
if [[ "$IMAGE_URL" == https://github.com/armbian/* ]]; then
203+
BASE_CACHE="https://cache.armbian.com/artifacts/${BOARD_SLUG}/archive/${IMAGE_NAME}"
204+
FILE_URL_ASC="${BASE_CACHE}.asc"
205+
FILE_URL_SHA="${BASE_CACHE}.sha"
206+
FILE_URL_TORRENT="${BASE_CACHE}.torrent"
207+
208+
REDI_URL_ASC="$FILE_URL_ASC"
209+
REDI_URL_SHA="$FILE_URL_SHA"
210+
REDI_URL_TORRENT="$FILE_URL_TORRENT"
211+
else
212+
FILE_URL_ASC="${IMAGE_URL}.asc"
213+
FILE_URL_SHA="${IMAGE_URL}.sha"
214+
FILE_URL_TORRENT="${IMAGE_URL}.torrent"
215+
216+
REDI_URL_ASC="${REDI_URL}.asc"
217+
REDI_URL_SHA="${REDI_URL}.sha"
218+
REDI_URL_TORRENT="${REDI_URL}.torrent"
219+
fi
220+
221+
EXPOSED=false
222+
while IFS= read -r exposed; do
223+
[[ -z "$exposed" ]] && continue
224+
[[ $IMAGE_URL =~ $exposed ]] && EXPOSED=true
225+
done < "${OS_DIR}/exposed.map"
226+
227+
echo "${BOARD_SLUG}|${BOARD_NAME}|${BOARD_VENDOR}|$ARMBIAN_VERSION|$IMAGE_URL|$FILE_URL_ASC|$FILE_URL_SHA|$FILE_URL_TORRENT|$REDI_URL|$REDI_URL_ASC|$REDI_URL_SHA|$REDI_URL_TORRENT|$IMAGE_CREATED|$IMAGE_SIZE|$IMAGE_RELEASE|$KERNEL_BRANCH|$IMAGE_VARIANT||$EXPOSED|$IMAGE_TYPE|$FILE_EXTENSION"
228+
done < "$feed"
229+
230+
} | jc --csv | jq '{"assets": .}' > "$OUT"
231+
232+
echo "✔ Generated $OUT"
233+
echo "✔ Assets: $(jq '.assets | length' "$OUT")"

0 commit comments

Comments
 (0)