Skip to content

Build

Build #1317

Workflow file for this run

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}