From 99f9d28a442a568f90e87f48bb5a40b9a493929a Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Thu, 21 May 2026 11:19:28 +0300 Subject: [PATCH] ci/image: downstream of build via workflow_run; manifest-driven URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit image.yml flips from a standalone cron at 23:30 UTC to triggering on build.yml's workflow_run completion. It no longer fires for nothing when build.yml's SHA-gate skips (PR #2111) and no longer runs against a stale `latest` tag when build.yml has been updated but the cron hasn't ticked yet. Changes: - Drop the independent 23:30 UTC cron; trigger via workflow_run on build.yml + workflow_dispatch as before. - Same partial-failure tolerance as manifest.yml (PR #2115): run on success OR failure, only skip cancelled. Missing firmware tgzs for a particular platform become silent skips inside create() rather than workflow-level abort. - New optional input: build_id (default: channels.nightly from the gh-pages manifest). Lets workflow_dispatch assemble flash images for any historic dated nightly without code changes — useful for recovering an older known-good image after a bisect identifies a regression. - Replace the hardcoded `releases/download/latest/openipc....tgz` with jq lookup against manifest.json (`.builds[] | select(.id==$b) | .platforms[$p].nor.url`). Returns empty for missing platforms; the existing create() skip-on-empty path handles that cleanly — matches pre-PR behaviour for boards that build.yml's matrix doesn't cover (e.g. ssc325_ultimate, t10_ultimate). - U-boot binaries continue to come from `releases/download/latest/ u-boot--nor.bin` because uboot.yml uploads to the latest tag and u-boot has its own (manual) build cadence — not coupled to the nightly firmware cycle. Verified locally: jq resolution against the live manifest gives the expected URLs for ssc335_{lite,ultimate}, t31_{lite,ultimate}, v851s_lite, and correctly returns empty for ssc325_ultimate / t10_ultimate (which the build matrix doesn't cover). PR-F (final) of six. With this merged the original plan from .claude/plans/let-s-think-about-how-fizzy-bubble.md is fully implemented. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/image.yml | 87 +++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 90244f4726..378bbc3c1d 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -1,36 +1,95 @@ name: image on: - schedule: - - cron: '30 23 * * *' + 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 -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 - TAG_NAME: image +permissions: + contents: write jobs: toolchain: name: Image runs-on: ubuntu-latest + # 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. + if: >- + github.event_name == '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: | - link=https://github.com/openipc/firmware/releases/download/latest + MANIFEST=/tmp/manifest.json + + # firmware_url + # -> 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 + # 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 - firmware=openipc.$2-nor-$3.tgz release=target/openipc-$1-nor-$3.bin mkdir -p output target - if ! wget -nv $link/$uboot -O output/$1.bin; then - echo -e "Download failed: $link/$uboot\n" + if ! wget -nv "$UBOOT_URL/$uboot" -O "output/$1.bin"; then + echo -e "Download failed: $UBOOT_URL/$uboot\n" return 0 fi - if ! wget -nv $link/$firmware -O output/$2.tgz; then - echo -e "Download failed: $link/$firmware\n" + 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 @@ -57,6 +116,6 @@ jobs: - name: Upload uses: softprops/action-gh-release@v2 with: - tag_name: ${{env.TAG_NAME}} + tag_name: image make_latest: false files: target/*.bin