Skip to content

image

image #840

Workflow file for this run

name: image
on:
workflow_run:
workflows: [build]
types: [completed]
workflow_dispatch:
inputs:
build_id:
description: 'Build ID to assemble (e.g. nightly-20260520-887328c). Default: channels.nightly from manifest.'
required: false
permissions:
contents: write
jobs:
toolchain:
name: Image
runs-on: ubuntu-latest
# Run after nightly (schedule) or operator dispatch only — PR builds do
# not produce new release assets, so reassembling images on every PR
# build is wasted compute. Mirrors the gate in manifest.yml.
#
# Publish on success AND on partial-failure: see manifest.yml rationale.
# A flaky board doesn't deny image assembly for the platforms that did
# upload a firmware tgz — firmware_url returns empty for missing ones and
# create() skips them.
#
# First clause: on a mirror the nightly's jobs skip, but the run still
# completes and fires workflow_run, so gate the cron-originated path on
# the canonical repo too — see build.yml's preflight. Dispatch-driven
# reassembly is untouched anywhere.
if: >-
(github.event.workflow_run.event != 'schedule' ||
github.repository == 'OpenIPC/firmware') &&
(github.event_name == 'workflow_dispatch' ||
((github.event.workflow_run.event == 'schedule' ||
github.event.workflow_run.event == 'workflow_dispatch') &&
contains(fromJSON('["success", "failure"]'), github.event.workflow_run.conclusion)))
env:
SIGMASTAR: ssc30kd ssc30kq ssc325 ssc333 ssc335 ssc335de ssc337 ssc337de ssc338q ssc377 ssc377d ssc377de ssc377qe ssc378de ssc378qe
INGENIC: t10 t10l t20 t20l t20x t21n t23n t30a t30a1 t30l t30n t30x t31a t31al t31l t31lc t31n t31x
ALLWINNER: v851s
MANIFEST_URL: https://openipc.github.io/firmware/manifest.json
UBOOT_URL: https://github.com/openipc/firmware/releases/download/latest
steps:
- name: Resolve build_id from manifest
id: resolve
env:
INPUT_BUILD_ID: ${{ inputs.build_id }}
run: |
set -eu
curl -fsSL "$MANIFEST_URL" -o /tmp/manifest.json
if [ -n "$INPUT_BUILD_ID" ]; then
BUILD_ID="$INPUT_BUILD_ID"
else
BUILD_ID=$(jq -r '.channels.nightly // ""' /tmp/manifest.json)
fi
if [ -z "$BUILD_ID" ] || [ "$BUILD_ID" = "null" ]; then
echo "::error::No build_id resolved (manifest channels.nightly empty? input not given?)"
exit 1
fi
# Sanity check: does the manifest actually know this build?
if ! jq -e --arg b "$BUILD_ID" '.builds[] | select(.id==$b)' /tmp/manifest.json >/dev/null; then
echo "::error::build_id $BUILD_ID is not present in manifest at $MANIFEST_URL"
exit 1
fi
echo "build_id=$BUILD_ID" >> "$GITHUB_OUTPUT"
echo "Assembling images for $BUILD_ID"
- name: Prepare
env:
BUILD_ID: ${{ steps.resolve.outputs.build_id }}
run: |
MANIFEST=/tmp/manifest.json
# firmware_url <firmware_soc> <variant>
# -> echoes the .nor URL for ${firmware_soc}_${variant} in
# $BUILD_ID, or nothing if the manifest has no such entry.
firmware_url() {
jq -r --arg b "$BUILD_ID" --arg p "${1}_${2}" \
'.builds[] | select(.id==$b) | .platforms[$p].nor.url // empty' \
"$MANIFEST"
}
# create <uboot_board> <firmware_soc> <variant>
# The two SoC arguments differ for Ingenic: u-boot is per
# board (t31al, t31lc, ...), firmware is per family (t31).
create() {
uboot=u-boot-$1-nor.bin
release=target/openipc-$1-nor-$3.bin
mkdir -p output target
if ! wget -nv "$UBOOT_URL/$uboot" -O "output/$1.bin"; then
echo -e "Download failed: $UBOOT_URL/$uboot\n"
return 0
fi
url=$(firmware_url "$2" "$3")
if [ -z "$url" ]; then
echo -e "Skip: no firmware in $BUILD_ID for ${2}_${3}\n"
return 0
fi
if ! wget -nv "$url" -O "output/$2.tgz"; then
echo -e "Download failed: $url\n"
return 0
fi
tar -xf output/$2.tgz -C output
dd if=/dev/zero bs=1K count=5000 status=none | tr '\000' '\377' > $release
dd if=output/$1.bin of=$release bs=1K seek=0 conv=notrunc status=none
dd if=output/uImage.$2 of=$release bs=1K seek=320 conv=notrunc status=none
dd if=output/rootfs.squashfs.$2 of=$release bs=1K seek=2368 conv=notrunc status=none
rm -rf output
echo -e "Created: $release\n"
}
# create_hisi <uboot_artifact> <firmware_soc> <variant> <release_tag> <fw_off_kib>
# HiSilicon V4/V5: u-boot is published per DDR binning (the
# boot-ROM DDR init table is baked into each boot image), and the
# firmware .tgz holds a single pre-packed kernel+rootfs blob
# (firmware.bin.<soc>) flashed at the SoC's firmware-partition
# offset. So assemble one full NOR image per DDR binning:
# u-boot at 0, firmware.bin at <fw_off_kib>.
create_hisi() {
local uboot="$1" soc="$2" variant="$3" tag="$4" off="$5"
local release="target/openipc-${tag}-nor-${variant}.bin"
mkdir -p output target
if ! wget -nv "$UBOOT_URL/$uboot" -O "output/$uboot"; then
echo -e "Download failed: $UBOOT_URL/$uboot\n"
return 0
fi
url=$(firmware_url "$soc" "$variant")
if [ -z "$url" ]; then
echo -e "Skip: no firmware in $BUILD_ID for ${soc}_${variant}\n"
return 0
fi
if ! wget -nv "$url" -O "output/$soc.tgz"; then
echo -e "Download failed: $url\n"
return 0
fi
tar -xf "output/$soc.tgz" -C output
# 16 MiB NOR, erased (0xff); u-boot at 0, firmware.bin at its
# partition offset (cv6xx MTDPARTS: firmware @ 0x50000 = 320 KiB).
dd if=/dev/zero bs=1K count=16384 status=none | tr '\000' '\377' > "$release"
dd if="output/$uboot" of="$release" bs=1K seek=0 conv=notrunc status=none
dd if="output/firmware.bin.$soc" of="$release" bs=1K seek="$off" conv=notrunc status=none
rm -rf output
echo -e "Created: $release\n"
}
for soc in $SIGMASTAR $ALLWINNER; do
create $soc $soc lite
create $soc $soc ultimate
done
for soc in $INGENIC; do
create $soc ${soc:0:3} lite
create $soc ${soc:0:3} ultimate
done
# HiSilicon Hi3516CV610/CV608 (cv6xx family). The kernel+rootfs blob
# is identical across DDR variants; only the per-binning u-boot (DDR
# init table) differs, so emit one full image per DDR topology.
# cv610 spans three topologies (DDR2-64M / DDR3-128M / DDR3-512M);
# cv608 is a single DDR2-64M part. firmware partition @ 0x50000.
# The 20s/00s u-boots are picked per DDR (their 20g/00g siblings
# carry the same DDR table, differing only in the socmodel env tag).
for variant in ultimate; do
create_hisi boot-hi3516cv610-10b-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr2-64m 320
create_hisi boot-hi3516cv610-20s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-128m 320
create_hisi boot-hi3516cv610-00s-nor.bin hi3516cv6xx "$variant" hi3516cv610-ddr3-512m 320
create_hisi boot-hi3516cv608-nor.bin hi3516cv6xx "$variant" hi3516cv608 320
done
# HiSilicon Hi3519DV500 (aarch64 V5). Same scheme: a single shared
# kernel+rootfs blob (firmware.bin.hi3519dv500) plus a per-binning
# u-boot whose baked DDR reg table differs by board layout — dmeb
# (6-layer) and dmebpro (4-layer flyby), both DDR4-2666 2 GB. NOR
# layout (16 MiB): u-boot @0, env @0x80000, firmware @0xC0000 = 768 KiB.
for variant in ultimate; do
create_hisi boot-hi3519dv500-dmeb-nor.bin hi3519dv500 "$variant" hi3519dv500-dmeb 768
create_hisi boot-hi3519dv500-dmebpro-nor.bin hi3519dv500 "$variant" hi3519dv500-dmebpro 768
done
- name: Upload
uses: softprops/action-gh-release@v2
with:
tag_name: image
make_latest: false
files: target/*.bin