Files
openipc-builder/.github/workflows/master.yml
T
a36a2d50aa ci: always rebuild on schedule (drop builder-HEAD-only dedupe) (#98)
The preflight gate skipped scheduled runs whenever this repo's HEAD
matched the SHA recorded in the previous nightly release body. That key
ignores every upstream input the build actually depends on:
OpenIPC/firmware HEAD, the toolchain release, the kernel tarball, the
vendor osdrv packages, and individual buildroot package upstreams.
builder.sh re-clones OpenIPC/firmware fresh on every run, so those
inputs do move between nightlies even when this repo is quiet.

The concrete failure mode this caused: on 2026-05-26 OpenIPC/firmware
bumped its hisilicon-opensdk pin to b958db4, which contains the V4
mipi_rx vsync IRQ fix (openhisilicon PR #195, closes #194 — torn
frames / 'Timeout from venc channel 0' on hi3516ev200, gk7205v200,
gk7205v210, gk7205v300, hi3516ev300 hardware). The next builder
nightly (2026-05-27 07:00 UTC) ran with this repo unchanged since
2026-05-25, so preflight skipped the entire Firmware matrix. Field
cameras (verified: gk7205v210_lite_vixand-ivg-g4h at IP redacted,
running BUILD_ID nightly-20260526-ee40da8, srcversion
8EF923DF907C1F7F3C6898E — the pre-fix open_mipi_rx fingerprint from
PR #195's verification table) kept pulling the stale 'latest' release
asset and kept showing the regression after sysupgrade.

A cross-input dedupe key would be possible (firmware HEAD + toolchain
URL hash + ...) but enumerates every upstream this build pulls. Simpler
and correct: scheduled nightlies always build. workflow_dispatch is
unaffected — that path was already unconditional.

Co-authored-by: Dmitry Ilyin <zekccsripfwrocp-15689@yn.furvionx.com>
2026-05-27 16:56:02 +03:00

328 lines
12 KiB
YAML

name: Build
on:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
inputs:
platform:
description: Platform
required: false
jobs:
preflight:
name: Preflight
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.gate.outputs.should_build }}
head_sha: ${{ steps.gate.outputs.head_sha }}
short_sha: ${{ steps.gate.outputs.short_sha }}
build_id: ${{ steps.gate.outputs.build_id }}
built_at: ${{ steps.gate.outputs.built_at }}
steps:
- uses: actions/checkout@v4
- id: gate
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
HEAD=$(git rev-parse HEAD)
SHORT=$(git rev-parse --short HEAD)
BUILD_ID="nightly-$(date -u +%Y%m%d)-${SHORT}"
BUILT_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# Nightly rebuilds unconditionally on schedule. builder.sh re-clones
# OpenIPC/firmware at HEAD on every run, so the relevant inputs are
# mostly outside this repo (firmware HEAD, toolchain release, kernel
# tarball, vendor osdrv, individual package upstreams). The previous
# gate skipped when only this repo's HEAD matched the last published
# nightly, which made upstream fixes (e.g. an openhisilicon mipi_rx
# regression fix that lands in firmware) invisible to users until
# something here happened to change. Always build on schedule.
echo "Build: $BUILD_ID (event=${{ github.event_name }})"
echo "should_build=true" >> "$GITHUB_OUTPUT"
echo "head_sha=$HEAD" >> "$GITHUB_OUTPUT"
echo "short_sha=$SHORT" >> "$GITHUB_OUTPUT"
echo "build_id=$BUILD_ID" >> "$GITHUB_OUTPUT"
echo "built_at=$BUILT_AT" >> "$GITHUB_OUTPUT"
buildroot:
name: Firmware
needs: preflight
if: needs.preflight.outputs.should_build == 'true'
runs-on: ubuntu-latest
env:
BUILD_ID: ${{ needs.preflight.outputs.build_id }}
BUILD_SHA: ${{ needs.preflight.outputs.head_sha }}
BUILD_PLATFORM: ${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
platform:
- gk7202v300_lite_cootli_camv0103
- gk7202v300_lite_ipg-g3-wr
- gk7202v300_lite_xg521
- gk7205v200_lite_tiandy-tc-c321n
- gk7205v200_lite_vixand-ipc-1
- gk7205v200_lite_vixand-iph-5-4g
- gk7205v210_lite_tiandy-tc-c32qn
- gk7205v210_lite_vixand-ivg-g3s
- gk7205v210_lite_vixand-ivg-g4f-a
- gk7205v210_lite_vixand-ivg-g4f-a-w
- gk7205v210_lite_vixand-ivg-g4h
- gk7205v300_lite_vixand-ivg-g6s
- gk7205v300_lite_vixand-ivg-g6s-w
- hi3516cv200_lite_trassir-tr-d4121ir1-v2
- hi3516ev300_ultimate_rostelecom-ipc8232swc-we
- hi3516ev300_ultimate_rvi-1ncmw2028
- hi3518ev200_lite_smartwares-cip-37210
- hi3518ev200_lite_switcam-hs303
- hi3518ev200_lite_switcam-hs303-v2
- hi3518ev200_lite_vstarcam-c8892wip
- hi3518ev200_lite_qtech-qvc-ipc-136w
- hi3518ev300_lite_bathhouse
- hi3518ev300_lite_xiaomi-mjsxj02hl
- hi3518ev200_ultimate_lenovo-snowman-1080p
- ssc30kd_lite_cmcc-ds-ytj5301
- ssc325_lite_imilab-ec3-cmsxj25a
- ssc325_lite_imou-c22cp
- ssc325_lite_tp-link-tapo-c310-v1
- ssc325_lite_trassir-tr-w2c1-v1
- ssc325de_lite_imou-c22ep-s2
- ssc333_lite_meari-speed-6s
- ssc333_lite_tp-link-tapo-c110-v2
- ssc333_lite_tp-link-tapo-c110-v26
- ssc333_lite_babysense-see-hd-ip206
- ssc333_lite_vstarcam-c43s_b
- ssc335_lite_tp-link-tapo-c110-v1
- ssc335_lite_tp-link-tapo-c310-v220
- ssc335_lite_trassir-tr-w2c1-v2
- ssc335de_lite_dahua-hfw1230sp-v2
- ssc335de_lite_imou-c22e-s2-v2
- ssc335de_lite_uniview-c1l-2wn-g
- ssc337_lite_h3c-tc2101
- ssc337_lite_tiandy-tc-c321n-v2
- ssc337_lite_tp-link-tapo-c110-v1
- ssc337de_ultimate_foscam-x5
- ssc338q_fpv_caddx-fly
- ssc338q_fpv_emax-wyvern-link
- ssc338q_fpv_openipc-mario-aio
- ssc338q_fpv_openipc-thinker-aio
- ssc338q_fpv_openipc-urllc-aio
- ssc338q_fpv_runcam-wifilink
- ssc377qe_fpv_ccdcam-im50q01-tipoman
- t10_lite_hb-wifi-z6
- t10_lite_jvs-ingt10-gqs60ep
- t20_ultimate_azarton-c1
- t20_lite_ec37-t11
- t31_lite_vstarcam-cs55
- t21_lite_chinamobile-hdc-51-a5-v12
- t21_lite_smartwares-cip-37210at
- t21_lite_xyx-06s
- t21_lite_wansview-q5-1080p
- t23_lite_jooan-a6m-u
- t23_lite_jooan-q3r-u
- t23_lite_lsc-3215672
- t31_lite_aceline-aip-o4
- t31_lite_wansview-q5-2k
- t31_lite_aoni-ep01j05
- t31_lite_chinamobile-hdc-51-a6-v11
- t31_lite_cmcc-hdc-51-a6-v10
- t31_lite_chinatelecom-y4h-50
- t31_lite_wyze-v3b
- t31_lite_xiaomi-mjsxj03hl
- t31_lite_xiaomi-mjsxj03hl-jxq03
- t31_lite_tuya-gv7630-t31-ptz
- t31_ultimate_azarton-c1-t31x
- t31_ultimate_gcraftsman-gca50
- t31_lite_zte-k540
- t40_lite_movols-mo-805p
# APFPV
- ssc30kq_apfpv_greg-generic-bu-eu
- ssc30kq_apfpv_greg-generic-cu-eu
- ssc338q_apfpv
- ssc338q_apfpv_greg-generic-bu-eu
- ssc338q_apfpv_greg-generic-cu-eu
- ssc378qe_apfpv
# FPV
- hi3516ev200_fpv
- hi3516ev300_fpv
- hi3536dv100_fpv
- gk7205v200_fpv
- gk7205v210_fpv
- gk7205v300_fpv
- ssc30kq_fpv
- ssc338q_fpv
- ssc378qe_fpv
# Ruby
- ssc30kq_rubyfpv_generic
- ssc338q_rubyfpv_generic
- ssc338q_rubyfpv_thinker_internal_wifi
# LTE
- hi3516ev200_lte
- hi3516ev300_lte
- gk7205v200_lte
- gk7205v300_lte
# Venc
- gk7205v200_venc
- gk7205v210_venc
- gk7205v300_venc
# Mini
- hi3516cv300_mini
- hi3518ev200_mini
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Prepare firmware
run: |
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
echo CACHE_DATE=$(date +%m) >> ${GITHUB_ENV}
INPUT=${{inputs.platform}}
MATRIX=$(echo ${{matrix.platform}} | cut -d_ -f1)
if [ -z ${INPUT} ] || [ ${INPUT} = ${MATRIX} ]; then
echo RUN=${MATRIX} >> ${GITHUB_ENV}
fi
- name: Setup ccache
if: env.RUN
uses: actions/cache@v4
with:
path: /tmp/ccache
key: ${{matrix.platform}}-${{env.CACHE_DATE}}
# builder.sh does `rm -rf openipc` at the top of every run to force a
# clean re-clone of OpenIPC/firmware, which also nukes the default
# `openipc/output/dl/` directory Buildroot uses for downloaded source
# tarballs. Without a cache, every cron run re-downloads ~1-3 GB of
# tarballs from upstream mirrors — and any one of them randomly
# failing (e.g. cgit `snapshot/` URLs that regenerate tarballs per
# request with shifting sha256, like wireguard-tools on
# git.zx2c4.com) takes down the matrix entry for that board. See
# kaeru wireguard-zx2c4-tarball-regeneration-hash-drift for the
# specific incident this caching addresses.
#
# Cache to /tmp/builder-dl (outside the openipc/ tree so builder.sh's
# rm -rf doesn't touch it) and point Buildroot at it via BR2_DL_DIR
# env var (honored per Buildroot manual section "Location of source
# tarballs"). Mirrors OpenIPC/firmware/.github/workflows/build.yml's
# output/dl cache, adapted for builder's clean-clone behavior.
- name: Setup dl cache
if: env.RUN
uses: actions/cache@v4
with:
path: /tmp/builder-dl
key: builder-dl-${{env.CACHE_DATE}}
restore-keys: builder-dl-
- name: Build firmware
if: env.RUN
run: |
export GIT_HASH=$(git rev-parse --short ${GITHUB_SHA})
export GIT_BRANCH=${GITHUB_REF_NAME}
echo GIT_HASH=${GIT_HASH} >> ${GITHUB_ENV}
echo GIT_BRANCH=${GIT_BRANCH} >> ${GITHUB_ENV}
mkdir -p /tmp/ccache
ln -s /tmp/ccache ${HOME}/.ccache
mkdir -p /tmp/builder-dl
export BR2_DL_DIR=/tmp/builder-dl
NAME=${{matrix.platform}}
# Retry budget for transient upstream toolchain/CDN flakes
# (matches OpenIPC/firmware/.github/workflows/build.yml).
backoffs="30 60 120 300 600 1200"
attempt=1
for sleep_for in $backoffs ""; do
bash builder.sh ${NAME} && break
if [ -z "$sleep_for" ]; then
echo "::error::build failed after ${attempt} attempts"
exit 1
fi
echo "::warning::attempt ${attempt} failed, retrying after ${sleep_for}s"
sleep "$sleep_for"
attempt=$((attempt + 1))
done
cd openipc
TIME=$(date -d @${SECONDS} +%M:%S)
echo TIME=${TIME} >> ${GITHUB_ENV}
COMMON=$(echo ${NAME} | awk -F_ '{print NF-1}')
NORFW=$(find output/images -name openipc*nor*)
if [ ! -z ${NORFW} ]; then
if [ ${COMMON} -eq 1 ]; then
echo NORFW=openipc/${NORFW} >> ${GITHUB_ENV}
else
mv ${NORFW} ../${NAME}-nor.tgz
echo NORFW=${NAME}-nor.tgz >> ${GITHUB_ENV}
fi
fi
NANDFW=$(find output/images -name openipc*nand*)
if [ ! -z ${NANDFW} ]; then
if [ ${COMMON} -eq 1 ]; then
echo NANDFW=openipc/${NANDFW} >> ${GITHUB_ENV}
else
mv ${NANDFW} ../${NAME}-nand.tgz
echo NANDFW=${NAME}-nand.tgz >> ${GITHUB_ENV}
fi
fi
if [ -z ${NORFW} ] && [ -z ${NANDFW} ]; then
exit 1
fi
- name: Upload firmware (dated)
if: env.NORFW || env.NANDFW
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.preflight.outputs.build_id }}
prerelease: true
body: |
sha=${{ needs.preflight.outputs.head_sha }}
short=${{ needs.preflight.outputs.short_sha }}
built_at=${{ needs.preflight.outputs.built_at }}
files: |
${{env.NORFW}}
${{env.NANDFW}}
- name: Upload firmware (rolling nightly)
if: env.NORFW || env.NANDFW
uses: softprops/action-gh-release@v2
with:
tag_name: nightly
body: |
sha=${{ needs.preflight.outputs.head_sha }}
short=${{ needs.preflight.outputs.short_sha }}
built_at=${{ needs.preflight.outputs.built_at }}
files: |
${{env.NORFW}}
${{env.NANDFW}}
- name: Upload firmware (latest — legacy alias)
if: env.NORFW || env.NANDFW
uses: softprops/action-gh-release@v2
with:
tag_name: latest
files: |
${{env.NORFW}}
${{env.NANDFW}}
- name: Send binary
if: env.NORFW
run: |
TG_MSG="Build: ${BUILD_ID}\nCommit: ${GIT_HASH}\nBranch: ${GIT_BRANCH}\nTime: ${TIME}\n\n"
TG_ICON="\xE2\x9C\x85 GitHub Actions"
TG_HEADER=$(echo -e ${TG_MSG}${TG_ICON})
TG_TOKEN=${{secrets.TELEGRAM_TOKEN_BOT_OPENIPC}}
TG_CHANNEL=${{secrets.TELEGRAM_CHANNEL_OPENIPC_DEV}}
HTTP=$(curl -s -o /dev/null -w %{http_code} https://api.telegram.org/bot${TG_TOKEN}/sendDocument -F chat_id=${TG_CHANNEL} -F caption="${TG_HEADER}" -F document=@${NORFW})
echo Telegram response: ${HTTP}