Skip to content

Build

Build #1192

Workflow file for this run

name: Build
on:
workflow_dispatch:
inputs:
skip_linter_check:
type: boolean
default: true
description: 'Skip waiting for the Linter workflow (default true for manual runs)'
branch:
type: string
default: ''
description: 'MTL branch, tag or full 40-char SHA to build; CI stays from the workflow branch (empty = workflow branch)'
push:
branches:
- main
- 'maint-**'
pull_request:
branches:
- main
- 'maint-**'
workflow_call:
inputs:
skip_linter_check:
type: boolean
default: false
description: 'Skip waiting for the Linter workflow (use for schedule/nightly triggers)'
branch:
type: string
default: ''
description: 'MTL branch, tag or full 40-char SHA to build; CI and tests stay from the workflow commit (empty = workflow ref)'
outputs:
mtl_commit:
description: 'Commit the MTL sources were hashed and built from'
value: ${{ jobs.checksums.outputs.commit }}
mtl_hash:
description: 'MTL source checksum'
value: ${{ jobs.checksums.outputs.mtl }}
jpegxs_hash:
description: 'JPEG XS source checksum'
value: ${{ jobs.checksums.outputs.jpegxs }}
ice_hash:
description: 'ICE source checksum'
value: ${{ jobs.checksums.outputs.ice }}
jpegxs_cache_key:
description: 'Exact JPEG XS cache key produced by the build runner'
value: ${{ jobs.build.outputs.jpegxs_cache_key }}
ice_cache_key:
description: 'Exact kernel-specific ICE cache key produced by the build runner'
value: ${{ jobs.build.outputs.ice_cache_key }}
ffmpeg_hash:
description: 'ffmpeg source checksum'
value: ${{ jobs.checksums.outputs.ffmpeg }}
gstreamer_hash:
description: 'gstreamer source checksum'
value: ${{ jobs.checksums.outputs.gstreamer }}
plugins_hash:
description: 'st22 avcodec plugin source checksum'
value: ${{ jobs.checksums.outputs.plugins }}
# One build per pull request at a time. The build job runs on the e835 host, so
# a superseded run is not just wasted minutes: it sits in front of the newest
# commit in the queue for a runner the whole hardware fleet shares. Five runs of
# this workflow queued behind one nightly is what prompted this.
#
# Keyed on the run id rather than the sha outside a pull request, because this
# workflow is also called by the nightly and custom pytest workflows, where
# github.workflow is the caller's name: two dispatches of the same commit are
# two separate intents and must not cancel each other.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
checks: read
jobs:
# ── Gate: skip the build when no artifact input changed ──
# Only a pull request or a push is filtered. A caller (nightly, custom pytest)
# always gets a build. build_workflow holds each filter that pr-gate.yml gets,
# so a test job never waits for a build that this gate skipped.
changes:
runs-on: ubuntu-22.04
permissions:
contents: read
pull-requests: read
outputs:
run: ${{ (github.event_name != 'pull_request' && github.event_name != 'push') || steps.filter.outputs.build_workflow == 'true' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
with:
filters: .github/path_filters.yml
# ── Gate: wait for every Linter check on this commit ──
# The names below are the *check run* names produced by linter.yml, which is the
# matrix job name and the residual job's `name:`. They are a contract between the
# two workflows: renaming a job there without renaming it here does not fail the
# gate, it makes the gate wait for something that never arrives and then time out.
# That is exactly how this broke once -- it still asked for super-linter's old
# "Lint Code Base" check, which stopped existing when checkpatch replaced it.
wait-for-linter:
needs: changes
if: ${{ needs.changes.outputs.run == 'true' && !inputs.skip_linter_check && github.ref_name != 'main' }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Wait for Linter
uses: ./.github/actions/wait-for-workflow
with:
check_name: |
checkpatch (ubuntu-latest)
checkpatch (macos-latest)
checkpatch (windows-latest)
Lint checks not yet in checkpatch
timeout: '900'
# ── Compute source checksums ──
checksums:
needs: changes
if: ${{ needs.changes.outputs.run == 'true' }}
runs-on: ubuntu-22.04
timeout-minutes: 10
outputs:
dpdk: ${{ steps.sums.outputs.dpdk }}
mtl: ${{ steps.sums.outputs.mtl }}
jpegxs: ${{ steps.sums.outputs.jpegxs }}
ice: ${{ steps.sums.outputs.ice }}
ffmpeg: ${{ steps.sums.outputs.ffmpeg }}
gstreamer: ${{ steps.sums.outputs.gstreamer }}
plugins: ${{ steps.sums.outputs.plugins }}
commit: ${{ steps.checkout.outputs.commit }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
# The branch is resolved to a commit here, once. The build job and the
# caller's test jobs check out that commit, never the branch again: the
# hashes below include .github/ files, so all of them must hash and
# build the same source commit with the same overlay, or the test jobs'
# fail-on-cache-miss restore finds nothing.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
id: checkout
with:
ref: ${{ inputs.branch }}
fetch-depth: ${{ inputs.branch && '0' || '1' }}
- name: Install Task
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
# Twice: the branch's own overlay-tests may be older than the workflow
# commit's. The first run checks out the workflow commit's copy, the
# second runs it.
- name: Overlay CI and tests from the workflow commit
if: inputs.branch != ''
env:
MTL_SOURCE: ${{ inputs.branch }}
TEST_SHA: ${{ github.sha }}
TEST_REF: ${{ github.ref_name }}
run: task ci:workflow -- overlay-tests && task ci:workflow -- overlay-tests
- id: sums
uses: ./.github/actions/source-checksums
build:
needs: [wait-for-linter, checksums]
if: ${{ !cancelled() && needs.checksums.result == 'success' && (needs.wait-for-linter.result == 'success' || needs.wait-for-linter.result == 'skipped') }}
runs-on: dpdk
timeout-minutes: 60
outputs:
jpegxs_cache_key: ${{ steps.keys.outputs.jpegxs_key }}
ice_cache_key: ${{ steps.keys.outputs.ice_key }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: Checkout MTL
uses: OpenVisualCloud/Media-Transport-Library/.github/actions/checkout-clean@2f540b33d05ab93541c816182b791c0fb6b5d143
with:
ref: ${{ needs.checksums.outputs.commit }}
fetch-depth: ${{ inputs.branch && '0' || '1' }}
- name: Install Task
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
# Twice: the branch's own overlay-tests may be older than the workflow
# commit's. The first run checks out the workflow commit's copy, the
# second runs it.
- name: Overlay CI and tests from the workflow commit
if: inputs.branch != ''
env:
MTL_SOURCE: ${{ inputs.branch }}
TEST_SHA: ${{ github.sha }}
TEST_REF: ${{ github.ref_name }}
run: task ci:workflow -- overlay-tests && task ci:workflow -- overlay-tests
# Before anything expensive: this runner is long-lived, and a missing
# libelf otherwise surfaces as a pkg-config failure deep inside the build.
# The build scope, because that is what this job consumes -- it builds no
# eBPF, so holding it to xdp-tools' prerequisites fails it on headers
# nothing it compiles includes.
- name: 'system: Check host prerequisites'
shell: bash
run: task ebpf:check-build
- name: Compute exact cache keys
id: keys
shell: bash
env:
HASH_DPDK: ${{ needs.checksums.outputs.dpdk }}
HASH_MTL: ${{ needs.checksums.outputs.mtl }}
HASH_JPEGXS: ${{ needs.checksums.outputs.jpegxs }}
HASH_ICE: ${{ needs.checksums.outputs.ice }}
HASH_FFMPEG: ${{ needs.checksums.outputs.ffmpeg }}
HASH_GSTREAMER: ${{ needs.checksums.outputs.gstreamer }}
HASH_PLUGINS: ${{ needs.checksums.outputs.plugins }}
run: task ci:cache-keys
- name: 'stash: Restore DPDK cache'
id: dpdk-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.dpdk_key }}
path: .local_install/dpdk
- name: 'stash: Restore MTL cache'
id: mtl-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.mtl_key }}
path: .local_install/mtl
- name: 'stash: Restore JPEG XS cache'
id: jpegxs-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.jpegxs_key }}
path: .local_install/jpegxs
- name: 'stash: Restore FFmpeg cache'
id: ffmpeg-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.ffmpeg_key }}
path: .local_install/ffmpeg
- name: 'stash: Restore GStreamer cache'
id: gstreamer-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.gstreamer_key }}
path: .local_install/gstreamer
- name: 'stash: Restore plugins cache'
id: plugins-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.plugins_key }}
path: .local_install/plugins
- name: 'stash: Restore ICE cache'
id: ice-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: ${{ steps.keys.outputs.ice_key }}
path: .local_install/ice
- name: Evaluate cache results
id: cache-state
shell: bash
env:
CACHE_HIT_DPDK: ${{ steps.dpdk-cache.outputs.cache-hit }}
CACHE_HIT_MTL: ${{ steps.mtl-cache.outputs.cache-hit }}
CACHE_HIT_JPEGXS: ${{ steps.jpegxs-cache.outputs.cache-hit }}
CACHE_HIT_FFMPEG: ${{ steps.ffmpeg-cache.outputs.cache-hit }}
CACHE_HIT_GSTREAMER: ${{ steps.gstreamer-cache.outputs.cache-hit }}
CACHE_HIT_PLUGINS: ${{ steps.plugins-cache.outputs.cache-hit }}
CACHE_HIT_ICE: ${{ steps.ice-cache.outputs.cache-hit }}
run: task ci:evaluate-caches
- name: Build missing dependencies
if: steps.cache-state.outputs.any_miss == '1'
shell: bash
run: task ci:build-dependencies
- name: Validate dependency outputs
shell: bash
run: task ci:validate-dependencies
- name: 'stash: Save DPDK cache'
if: success() && steps.cache-state.outputs.dpdk_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.dpdk_key }}
path: .local_install/dpdk
- name: 'stash: Save MTL cache'
if: success() && steps.cache-state.outputs.mtl_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.mtl_key }}
path: .local_install/mtl
- name: 'stash: Save JPEG XS cache'
if: success() && steps.cache-state.outputs.jpegxs_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.jpegxs_key }}
path: .local_install/jpegxs
- name: 'stash: Save FFmpeg cache'
if: success() && steps.cache-state.outputs.ffmpeg_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.ffmpeg_key }}
path: .local_install/ffmpeg
- name: 'stash: Save GStreamer cache'
if: success() && steps.cache-state.outputs.gstreamer_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.gstreamer_key }}
path: .local_install/gstreamer
- name: 'stash: Save plugins cache'
if: success() && steps.cache-state.outputs.plugins_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.plugins_key }}
path: .local_install/plugins
- name: 'stash: Save ICE cache'
if: success() && steps.cache-state.outputs.ice_miss == '1'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
key: ${{ steps.keys.outputs.ice_key }}
path: .local_install/ice
# The one check to require in branch protection. It always runs, so it
# reports even when the path filter skips build jobs, and it fails only if a
# job it needs failed or was cancelled.
build-result:
name: build-result
needs: [changes, wait-for-linter, checksums, build]
if: always()
runs-on: ubuntu-latest
steps:
- name: All build jobs ok
if: ${{ !(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
run: exit 0
- name: Some build jobs failed
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1