image #835
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |