Skip to content

Add: Cix-ACPI board image & Cixtech Vendor image (#237) #19

Add: Cix-ACPI board image & Cixtech Vendor image (#237)

Add: Cix-ACPI board image & Cixtech Vendor image (#237) #19

name: "Assets: Generate board & vendor thumbnails"
on:
push:
branches:
- main
paths:
- "board-images/**"
- "board-vendor-logos/**"
workflow_dispatch:
env:
BOARDS_PATH: "build/config/boards"
THUMB_WIDTHS: "100 150 272 300 360 480 768 960 1024 1920"
SHARDS: 4
# Source directories in this repo
BOARD_IMAGES_DIR: "board-images"
VENDOR_LOGOS_DIR: "board-vendor-logos"
concurrency:
group: board-images
cancel-in-progress: false
jobs:
Check:
name: "Check permissions"
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'Armbian' }}
steps:
- name: "Check permissions"
uses: armbian/actions/team-check@main
with:
ORG_MEMBERS: ${{ secrets.ORG_MEMBERS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TEAM: "Release manager"
Boards-index:
name: "Build boards matrix"
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'Armbian' }}
needs: Check
outputs:
matrix: ${{ steps.boards.outputs.JSON_CONTENT }}
config_matrix: ${{ steps.board-configs.outputs.JSON_CONTENT }}
steps:
- name: "Checkout armbian/github.io"
uses: actions/checkout@v6
- name: "Checkout armbian/build"
uses: actions/checkout@v6
with:
repository: armbian/build
path: build
- name: "Generate boards JSON matrix from board-images directory"
id: boards
run: |
set -euo pipefail
if [[ ! -d "${BOARD_IMAGES_DIR}" ]]; then
echo "::warning ::board-images directory not found"
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
echo '[]' >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
exit 0
fi
mapfile -t boards < <(
find "${BOARD_IMAGES_DIR}" -maxdepth 1 -type f -iname "*.png" \
-printf '%f\n' \
| sed -E 's/\.png$//' \
| sed 's/\(.*\)/\L\1/' \
| sort
) || declare -a boards=()
echo "Found ${#boards[@]} board images"
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
printf '%s\n' "${boards[@]}" | awk 'NF' | jq -R . | jq -s . >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: "Generate board configs JSON matrix from build config"
id: board-configs
run: |
set -euo pipefail
cd "${BOARDS_PATH}"
mapfile -t boards < <(
find . -maxdepth 1 -type f \
\( -name "*.conf" -o -name "*.csc" -o -name "*.wip" -o -name "*.tvb" -o -name "*.eos" \) \
-printf '%f\n' \
| sed -E 's/\.(conf|csc|wip|tvb|eos)$//' \
| sed 's/\(.*\)/\L\1/' \
| sort
) || declare -a boards=()
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
printf '%s\n' "${boards[@]}" | awk 'NF' | jq -R . | jq -s . >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
Vendors-index:
name: "Build vendors matrix"
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'Armbian' }}
needs: Check
outputs:
matrix: ${{ steps.vendors.outputs.JSON_CONTENT }}
config_matrix: ${{ steps.vendor-configs.outputs.JSON_CONTENT }}
steps:
- name: "Checkout armbian/github.io"
uses: actions/checkout@v6
- name: "Checkout armbian/build"
uses: actions/checkout@v6
with:
repository: armbian/build
path: build
- name: "Generate vendors JSON matrix from board-vendor-logos directory"
id: vendors
run: |
set -euo pipefail
if [[ ! -d "${VENDOR_LOGOS_DIR}" ]]; then
echo "::warning ::board-vendor-logos directory not found"
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
echo '[]' >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
exit 0
fi
mapfile -t vendors < <(
find "${VENDOR_LOGOS_DIR}" -maxdepth 1 -type f \
\( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.svg" \) \
-printf '%f\n' \
| sed -E '
s/\.(png|jpg|jpeg|svg)$//;
s/[-_.]?logo[-_.]?//gi;
s/-[0-9]+x[0-9]+$//;
' \
| sed 's/\(.*\)/\L\1/' \
| tr '_' '-' \
| sort -u
) || declare -a vendors=()
echo "Found ${#vendors[@]} vendor logo files"
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
printf '%s\n' "${vendors[@]}" | awk 'NF' | jq -R . | jq -s . >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
- name: "Generate vendor configs JSON matrix from build config"
id: vendor-configs
run: |
set -euo pipefail
cd "${BOARDS_PATH}"
mapfile -t vendors < <(
find . -maxdepth 1 -type f \
\( -name "*.conf" -o -name "*.csc" -o -name "*.wip" -o -name "*.tvb" -o -name "*.eos" \) \
-exec grep -h 'BOARD_VENDOR=' {} + 2>/dev/null \
| sed -E '
s/.*BOARD_VENDOR=//;
s/^["'\'']//; s/["'\'']$//;
' \
| tr '[:upper:]' '[:lower:]' \
| tr '_' '-' \
| awk 'NF' \
| sort -u
) || declare -a vendors=()
echo 'JSON_CONTENT<<EOF' >> "$GITHUB_OUTPUT"
printf '%s\n' "${vendors[@]}" | awk 'NF' | jq -R . | jq -s . >> "$GITHUB_OUTPUT"
echo 'EOF' >> "$GITHUB_OUTPUT"
Generate-images:
name: "Maker board & vendor pics"
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'Armbian' }}
needs: [Boards-index, Vendors-index]
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3]
env:
BOARDS_JSON: ${{ needs.Boards-index.outputs.matrix }}
BOARDS_CONFIG_JSON: ${{ needs.Boards-index.outputs.config_matrix }}
VENDORS_JSON: ${{ needs.Vendors-index.outputs.matrix }}
VENDORS_CONFIG_JSON: ${{ needs.Vendors-index.outputs.config_matrix }}
steps:
- name: "Checkout armbian.github.io"
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: "Install SSH key"
uses: shimataro/ssh-key-action@v2
with:
key: "${{ secrets.KEY_UPLOAD }}"
known_hosts: "${{ secrets.KNOWN_HOSTS_ARMBIAN_UPLOAD }}"
if_key_exists: replace
- name: "Install GraphicsMagick"
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y graphicsmagick pngquant
- name: "Process boards & vendors in shard ${{ matrix.shard }}"
run: |
set -euo pipefail
SHARD="${{ matrix.shard }}"
SHARDS="${SHARDS}"
WIDTHS="${THUMB_WIDTHS}"
BOARD_DIR="${BOARD_IMAGES_DIR}"
VENDOR_DIR="${VENDOR_LOGOS_DIR}"
echo "Shard: ${SHARD}/${SHARDS}"
echo "Widths: ${WIDTHS}"
echo "Board images dir: ${BOARD_DIR}"
echo "Vendor logos dir: ${VENDOR_DIR}"
[[ -d "${BOARD_DIR}" ]] || echo "::warning ::Missing ${BOARD_DIR} directory"
[[ -d "${VENDOR_DIR}" ]] || echo "::warning ::Missing ${VENDOR_DIR} directory"
# -------------------------
# COLLECT DATA FOR AGGREGATION
# -------------------------
# Create shard-data directory
mkdir -p shard-data
# Save full JSON data for summary job to analyze
echo "${BOARDS_JSON}" > shard-data/boards-images.json
echo "${BOARDS_CONFIG_JSON}" > shard-data/boards-configs.json
echo "${VENDORS_JSON}" > shard-data/vendors-images.json
echo "${VENDORS_CONFIG_JSON}" > shard-data/vendors-configs.json
# Track processed/failed items (this shard only)
> shard-data/processed-boards.txt
> shard-data/processed-vendors.txt
> shard-data/failed-boards.txt
> shard-data/failed-vendors.txt
# -------------------------
# PROCESS BOARDS (all images present)
# -------------------------
echo "${BOARDS_JSON}" | jq -r '.[]' > all-boards.txt
idx=0
while IFS= read -r BOARD; do
if (( idx % SHARDS != SHARD )); then
idx=$((idx + 1))
continue
fi
echo "==== [${idx}] Board: ${BOARD} ===="
mapfile -t matches < <(find "${BOARD_DIR}" -type f -iname "${BOARD}.png" | sort || true)
if [[ "${#matches[@]}" -eq 0 ]]; then
echo "${BOARD}" >> shard-data/failed-boards.txt
idx=$((idx + 1))
continue
fi
ORIGINAL="${matches[0]}"
mkdir -p "output/images/original"
cp --update=none -- "${ORIGINAL}" "output/images/original/${BOARD}.png"
for width in ${WIDTHS}; do
mkdir -p "output/images/${width}"
OUT="output/images/${width}/${BOARD}.png"
echo "Generating ${OUT}"
if ! gm convert "${ORIGINAL}" \
-resize "${width}>" \
-strip \
-quality 90 \
"${OUT}"; then
echo "WARN: convert failed for ${BOARD} width ${width}" >&2
continue
fi
if command -v pngquant >/dev/null 2>&1; then
pngquant --quality=65-85 --speed 1 --force --output "${OUT}" "${OUT}" || \
echo "WARN: pngquant failed for ${BOARD} width ${width}" >&2
fi
done
echo "${BOARD}" >> shard-data/processed-boards.txt
idx=$((idx + 1))
done < all-boards.txt
# -------------------------
# PROCESS VENDORS (all logos present)
# -------------------------
if [[ -d "${VENDOR_DIR}" ]]; then
echo "${VENDORS_JSON}" | jq -r '.[]' > all-vendors.txt
vidx=0
while IFS= read -r VENDOR; do
if (( vidx % SHARDS != SHARD )); then
vidx=$((vidx + 1))
continue
fi
echo "==== [${vidx}] Vendor: ${VENDOR} ===="
# Find source logo:
# - starts with vendor
# - contains "logo"
# - skips -AxB.<ext>
mapfile -t vmatches < <(
find "${VENDOR_DIR}" -type f \
\( -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.svg" \) \
| awk -v v="${VENDOR}" 'BEGIN{IGNORECASE=1}{
b=$0; sub(/^.*\//,"",b);
if (b ~ "^" v "[-_.]" && b ~ /logo/ && b !~ /-[0-9]+x[0-9]+\.[^.]+$/) print $0
}' \
| sort
)
if [[ "${#vmatches[@]}" -eq 0 ]]; then
echo "${VENDOR}" >> shard-data/failed-vendors.txt
vidx=$((vidx + 1))
continue
fi
VORIG="${vmatches[0]}"
out_name="$(echo "${VENDOR}.png" | tr '[:upper:]' '[:lower:]' | tr '_' '-')"
mkdir -p "output/images/vendors/original"
# Normalize original to PNG (SVG rendered with transparency)
if [[ "${VORIG,,}" == *.svg ]]; then
if ! gm convert -background none -density 300 "${VORIG}" -strip "output/images/vendors/original/${out_name}"; then
echo "WARN: failed to render SVG for ${VENDOR}" >&2
echo "${VENDOR}" >> shard-data/failed-vendors.txt
vidx=$((vidx + 1))
continue
fi
else
if ! gm convert "${VORIG}" -strip "output/images/vendors/original/${out_name}"; then
echo "WARN: failed to normalize logo for ${VENDOR}" >&2
echo "${VENDOR}" >> shard-data/failed-vendors.txt
vidx=$((vidx + 1))
continue
fi
fi
for width in ${WIDTHS}; do
mkdir -p "output/images/vendors/${width}"
OUT="output/images/vendors/${width}/${out_name}"
echo "Generating ${OUT}"
if ! gm convert "output/images/vendors/original/${out_name}" \
-background none \
-resize "${width}>" \
-strip \
-define png:compression-level=9 \
"${OUT}"; then
echo "WARN: convert failed for vendor ${VENDOR} width ${width}" >&2
continue
fi
if command -v pngquant >/dev/null 2>&1; then
pngquant --quality=65-85 --speed 1 --force --output "${OUT}" "${OUT}" || \
echo "WARN: pngquant failed for vendor ${VENDOR} width ${width}" >&2
fi
# Generate version with 1px grey border
OUT_BORDER="output/images/vendors/${width}/${out_name%.png}-border.png"
echo "Generating ${OUT_BORDER}"
# Get image dimensions after resize
dimensions=$(gm identify -format "%w %h" "${OUT}" 2>/dev/null || echo "0 0")
read -r img_w img_h <<< "${dimensions}"
if [[ "${img_w}" -eq 0 ]] || [[ "${img_h}" -eq 0 ]]; then
echo "WARN: failed to get dimensions for vendor ${VENDOR} width ${width}" >&2
continue
fi
# Border settings
border_px=1
iw=$((img_w - 2 * border_px))
ih=$((img_h - 2 * border_px))
if ! gm convert "output/images/vendors/original/${out_name}" \
-resize "${img_w}x${img_h}" \
-background transparent -gravity center -extent "${img_w}x${img_h}" \
-matte \
-fill none \
-stroke gray50 \
-strokewidth ${border_px} \
-draw "rectangle 0,0 $((img_w - 1)),$((img_h - 1))" \
-strip \
-define png:compression-level=9 \
"${OUT_BORDER}"; then
echo "WARN: convert failed for vendor ${VENDOR} border width ${width}" >&2
continue
fi
if command -v pngquant >/dev/null 2>&1; then
pngquant --quality=65-85 --speed 1 --force --output "${OUT_BORDER}" "${OUT_BORDER}" || \
echo "WARN: pngquant failed for vendor ${VENDOR} border width ${width}" >&2
fi
done
echo "${VENDOR}" >> shard-data/processed-vendors.txt
vidx=$((vidx + 1))
done < all-vendors.txt
fi
- name: "Upload shard data"
uses: actions/upload-artifact@v6
with:
name: shard-data-${{ matrix.shard }}
path: shard-data/
retention-days: 1
- name: "Upload images to cache servers"
run: |
set -euo pipefail
curl -sS \
-H "Authorization: Token ${{ secrets.NETBOX_TOKEN }}" \
-H "Accept: application/json; indent=4" \
"${{ secrets.NETBOX_API }}/virtualization/virtual-machines/?limit=500&name__empty=false&status=active" \
| jq '.results[]
| select([.tags[].name] | index("cache"))
| {id, name, custom_fields}' > servers.json
if [[ ! -s servers.json ]]; then
echo "No cache servers returned from NetBox query, nothing to upload."
exit 0
fi
for row in $(jq -r '@base64' servers.json); do
_jq() { echo "${row}" | base64 --decode | jq -r "${1}"; }
id=$(_jq '.id')
name=$(_jq '.name')
path=$(_jq '.custom_fields.path')
port=$(_jq '.custom_fields.port')
username=$(_jq '.custom_fields.username')
echo "Uploading images to ${username}@${name}:${path}/cache/images (VM ID: ${id})"
rsync -e "ssh -p ${port} -o StrictHostKeyChecking=accept-new" \
-rvP output/images/ "${username}@${name}:${path}/cache/images"
done
Summary:
name: "Generate summary report"
runs-on: ubuntu-24.04
if: ${{ github.repository_owner == 'Armbian' }}
needs: [Boards-index, Vendors-index, Generate-images]
steps:
- name: "Download all shard data"
uses: actions/download-artifact@v7
with:
path: shard-data
- name: "Generate summary report"
run: |
set -euo pipefail
# Get JSON data from any shard (they're all identical)
for shard in shard-data/shard-data-*; do
if [[ -f "$shard/boards-images.json" ]]; then
BOARDS_IMAGES=$(cat "$shard/boards-images.json")
BOARDS_CONFIGS=$(cat "$shard/boards-configs.json")
VENDORS_IMAGES=$(cat "$shard/vendors-images.json")
VENDORS_CONFIGS=$(cat "$shard/vendors-configs.json")
break
fi
done
# Aggregate processed/failed from all shards
{
for shard in shard-data/shard-data-*/processed-boards.txt; do
[[ -f "$shard" ]] && cat "$shard"
done
} | sort -u | grep -v '^$' > all-processed-boards.txt || true
{
for shard in shard-data/shard-data-*/failed-boards.txt; do
[[ -f "$shard" ]] && cat "$shard"
done
} | sort -u | grep -v '^$' > all-failed-boards.txt || true
{
for shard in shard-data/shard-data-*/processed-vendors.txt; do
[[ -f "$shard" ]] && cat "$shard"
done
} | sort -u | grep -v '^$' > all-processed-vendors.txt || true
{
for shard in shard-data/shard-data-*/failed-vendors.txt; do
[[ -f "$shard" ]] && cat "$shard"
done
} | sort -u | grep -v '^$' > all-failed-vendors.txt || true
# Calculate missing pairs
echo "$BOARDS_CONFIGS" | jq -r '.[]' | awk 'NF' | sort > all-configs.txt
echo "$BOARDS_IMAGES" | jq -r '.[]' | awk 'NF' | sort > all-images.txt
echo "$VENDORS_CONFIGS" | jq -r '.[]' | awk 'NF' | sort > all-vendor-configs.txt
echo "$VENDORS_IMAGES" | jq -r '.[]' | awk 'NF' | sort > all-vendor-images.txt
# Build report
{
echo "# Board & Vendor Image Processing Summary"
echo ""
echo "Generated on: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
echo ""
echo "---"
echo ""
# Statistics
total_images=$(echo "$BOARDS_IMAGES" | jq '. | length')
total_configs=$(echo "$BOARDS_CONFIGS" | jq '. | length')
total_processed=$(wc -l < all-processed-boards.txt | awk '{print $1}')
total_failed=$(wc -l < all-failed-boards.txt | awk '{print $1}')
echo "## Statistics"
echo ""
echo "- **Board images found:** ${total_images}"
echo "- **Board configs found:** ${total_configs}"
echo "- **Successfully processed:** ${total_processed}"
echo "- **Failed to process:** ${total_failed}"
echo ""
# Board comparison
echo "## Board comparison"
echo ""
# Configs without images
comm -23 all-configs.txt all-images.txt > configs-no-images.txt
count=$(wc -l < configs-no-images.txt | awk '{print $1}')
echo "### Board configs without images (${count})"
echo ""
if [[ -s configs-no-images.txt ]]; then
awk 'NF' configs-no-images.txt | sed 's/^/- &/'
else
echo "None"
fi
echo ""
# Images without configs
comm -13 all-configs.txt all-images.txt > images-no-configs.txt
count=$(wc -l < images-no-configs.txt | awk '{print $1}')
echo "### Board images without configs (${count})"
echo ""
if [[ -s images-no-configs.txt ]]; then
awk 'NF' images-no-configs.txt | sed 's/^/- &/'
else
echo "None"
fi
echo ""
# Vendor comparison
echo "## Vendor comparison"
echo ""
# Configs without logos
comm -23 all-vendor-configs.txt all-vendor-images.txt > vendor-configs-no-logos.txt
count=$(wc -l < vendor-configs-no-logos.txt | awk '{print $1}')
echo "### Vendor configs without logos (${count})"
echo ""
if [[ -s vendor-configs-no-logos.txt ]]; then
awk 'NF' vendor-configs-no-logos.txt | sed 's/^/- &/'
else
echo "None"
fi
echo ""
# Logos without configs
comm -13 all-vendor-configs.txt all-vendor-images.txt > vendor-logos-no-configs.txt
count=$(wc -l < vendor-logos-no-configs.txt | awk '{print $1}')
echo "### Vendor logos without configs (${count})"
echo ""
if [[ -s vendor-logos-no-configs.txt ]]; then
awk 'NF' vendor-logos-no-configs.txt | sed 's/^/- &/'
else
echo "None"
fi
echo ""
# Processing failures
echo "## Processing failures"
echo ""
count=$(wc -l < all-failed-boards.txt | awk '{print $1}')
echo "### Boards failed to process (${count})"
echo ""
if [[ -s all-failed-boards.txt ]]; then
awk 'NF' all-failed-boards.txt | sed 's/^/- &/'
else
echo "None"
fi
echo ""
count=$(wc -l < all-failed-vendors.txt | awk '{print $1}')
echo "### Vendors failed to process (${count})"
echo ""
if [[ -s all-failed-vendors.txt ]]; then
awk 'NF' all-failed-vendors.txt | sed 's/^/- &/'
else
echo "None"
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: "Remove shard data artifacts"
uses: geekyeggo/delete-artifact@v5
with:
name: shard-data-*
failOnError: false