Skip to content

Merge pull request #44 from bstkhq/agent/frame-pool-test-lifecycle #109

Merge pull request #44 from bstkhq/agent/frame-pool-test-lifecycle

Merge pull request #44 from bstkhq/agent/frame-pool-test-lifecycle #109

Workflow file for this run

# Continuous integration for go-ffmpeg-ffi
name: CI
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
env:
GO_VERSION: '1.23'
jobs:
shim-contract:
runs-on: ubuntu-latest
name: Verify shim contract
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Reject stale C/Go contract output
run: go run ./internal/shim/cmd/gencontract -check
# Test without FFmpeg (should pass - tests mock/skip FFmpeg deps)
test-no-ffmpeg:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Run tests without FFmpeg
run: go test -v -short ./...
env:
CGO_ENABLED: "0"
# Compile the complete module and every test binary with the pinned Android
# NDK. Runtime and hardware-codec qualification are separate device gates.
android-compile:
runs-on: ubuntu-latest
name: Compile (Android API 33, ${{ matrix.arch }})
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
arch: [arm64, amd64]
env:
ANDROID_API: "33"
ANDROID_NDK_VERSION: "26.3.11579264"
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
# Ebitengine 2.9.9 declares Go 1.24. The binding itself retains its
# older language floor and is compiled by the other jobs as well.
go-version: '1.24'
- name: Install pinned Android NDK
run: |
sdk_root="${ANDROID_SDK_ROOT:-$ANDROID_HOME}"
set +o pipefail
yes | "$sdk_root/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null
set -o pipefail
"$sdk_root/cmdline-tools/latest/bin/sdkmanager" "ndk;$ANDROID_NDK_VERSION"
- name: Compile packages and tests
run: ./scripts/verify-android-build.sh "${{ matrix.arch }}"
- name: Install pinned Ebitengine mobile binder
run: go install github.com/hajimehoshi/ebiten/v2/cmd/ebitenmobile@v2.9.9
- name: Bind downstream Ebitengine fixture
run: |
output="$GITHUB_WORKSPACE/.build/android-ebiten/${{ matrix.arch }}/game.aar"
mkdir -p "$(dirname "$output")"
cd integration/android-ebiten
ebitenmobile bind \
-target "android/${{ matrix.arch }}" \
-javapkg dev.bstk.ffmpeg.probe.corelib \
-o "$output" .
test -s "$output"
# PureGo requires CGO on iOS. Compile the complete module for the physical
# device and both simulator architectures, then bind a downstream
# Ebitengine fixture into the same XCFramework shape consumed by Xcode apps.
ios-compile:
runs-on: macos-14
name: Compile (iOS 13, device + simulator)
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
# Ebitengine 2.9.9 declares Go 1.24.
go-version: '1.24'
cache-dependency-path: |
go.sum
integration/ios-ebiten/go.sum
- name: Record Xcode toolchain
run: |
xcodebuild -version
xcrun --sdk iphoneos --show-sdk-version
xcrun --sdk iphonesimulator --show-sdk-version
- name: Compile packages and tests for iPhone ARM64
run: ./scripts/verify-ios-build.sh iphoneos arm64
- name: Compile packages and tests for simulator ARM64
run: ./scripts/verify-ios-build.sh iphonesimulator arm64
- name: Compile packages and tests for simulator x86-64
run: ./scripts/verify-ios-build.sh iphonesimulator amd64
- name: Install pinned Ebitengine mobile binder
run: go install github.com/hajimehoshi/ebiten/v2/cmd/ebitenmobile@v2.9.9
- name: Bind downstream Ebitengine fixture
working-directory: integration/ios-ebiten
run: make compile
- name: Verify XCFramework slices
run: |
info=integration/ios-ebiten/.build/FFmpegProbe.xcframework/Info.plist
plutil -lint "$info"
plutil -convert json -o - "$info" | grep -q 'ios-arm64'
plutil -convert json -o - "$info" | grep -q 'ios-arm64_x86_64-simulator'
# Keep the complete desktop package and test surface buildable without CGO.
# Native runtime jobs below qualify the architectures for which GitHub
# provides runners; Windows ARM64 remains a compile-only target.
desktop-compile:
runs-on: ubuntu-latest
name: Compile (${{ matrix.goos }}/${{ matrix.goarch }})
strategy:
fail-fast: false
matrix:
include:
- goos: windows
goarch: amd64
- goos: windows
goarch: arm64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Compile packages and tests
run: go test -exec /bin/true -run '^$' ./...
env:
CGO_ENABLED: "0"
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
# Test every supported FFmpeg release line on one pinned platform.
test-with-ffmpeg-linux:
runs-on: ubuntu-latest
name: Test (Linux amd64, FFmpeg ${{ matrix.ffmpeg }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- ffmpeg: '6.0.1'
major: '6'
sha256: '9b16b8731d78e596b4be0d720428ca42df642bb2d78342881ff7f5bc29fc9623'
- ffmpeg: '6.1.6'
major: '6'
sha256: 'd4fcb164028dd3beee5d92c0ac72e46aac6973c75ea12dc14de07bf8f407370a'
- ffmpeg: '7.0.3'
major: '7'
sha256: 'bbb18f3cc5c1c34b21cf176c0168c4d9aa97697542a97f60d491a58a814dea65'
- ffmpeg: '7.1.5'
major: '7'
sha256: 'de668509caf9e35e3cd162473441fdb29538c6d96ed080292b3cf9e6fc5d558f'
- ffmpeg: '8.0.3'
major: '8'
sha256: '6136812ea6d4e68bdba27e33c2a94382711cdf4f8602ffef056ff792bd6f9818'
- ffmpeg: '8.1.2'
major: '8'
sha256: '464beb5e7bf0c311e68b45ae2f04e9cc2af88851abb4082231742a74d97b524c'
- ffmpeg: '9.0.1'
major: '9'
sha256: 'cf38e0e28c7e5605942c4a77755349b0145804a397af37eb1fb4c77cb237f635'
env:
FFMPEG_PREFIX: ${{ github.workspace }}/.ffmpeg/${{ matrix.ffmpeg }}
FFMPEG_EXPECTED_MAJOR: ${{ matrix.major }}
steps:
- uses: actions/checkout@v4
- name: Prepare workspace temp directory
run: |
mkdir -p .work/tmp
echo "TMPDIR=$GITHUB_WORKSPACE/.work/tmp" >> "$GITHUB_ENV"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential curl nasm pkg-config libx264-dev zlib1g-dev
- name: Restore FFmpeg build
id: ffmpeg-cache
uses: actions/cache@v4
with:
path: ${{ env.FFMPEG_PREFIX }}
key: ffmpeg-linux-amd64-${{ matrix.ffmpeg }}-v1
- name: Build FFmpeg ${{ matrix.ffmpeg }}
if: steps.ffmpeg-cache.outputs.cache-hit != 'true'
run: |
mkdir -p .work/ffmpeg-source "$FFMPEG_PREFIX" "$TMPDIR"
curl --fail --location --retry 3 \
--output .work/ffmpeg.tar.xz \
"https://ffmpeg.org/releases/ffmpeg-${{ matrix.ffmpeg }}.tar.xz"
echo "${{ matrix.sha256 }} .work/ffmpeg.tar.xz" | sha256sum --check --strict
tar -xf .work/ffmpeg.tar.xz --strip-components=1 -C .work/ffmpeg-source
cd .work/ffmpeg-source
./configure \
--prefix="$FFMPEG_PREFIX" \
--enable-shared \
--disable-static \
--disable-doc \
--disable-debug \
--disable-autodetect \
--disable-network \
--disable-ffplay \
--enable-gpl \
--enable-libx264 \
--enable-zlib
make -j4
make install
- name: Select FFmpeg ${{ matrix.ffmpeg }}
run: |
echo "$FFMPEG_PREFIX/bin" >> "$GITHUB_PATH"
echo "LD_LIBRARY_PATH=$FFMPEG_PREFIX/lib" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig" >> "$GITHUB_ENV"
- name: Verify selected FFmpeg release
run: ffmpeg -version | head -n 1 | grep -F "ffmpeg version ${{ matrix.ffmpeg }} "
- name: Verify selected FFmpeg runtime
run: go test -count=1 -run '^TestConfiguredFFmpegFamily$' -v ./internal/bindings
env:
CGO_ENABLED: "0"
- name: Verify FFmpeg ABI layout
run: |
mkdir -p .work "$TMPDIR"
cc -std=c11 -Wall -Wextra -Werror \
$(pkg-config --cflags libavutil libavcodec libavformat libavfilter libavdevice libswscale libswresample) \
internal/abi/testdata/layout_probe.c \
-o .work/layout-probe
.work/layout-probe > .work/layout.txt
go run ./internal/abi/cmd/check-layout < .work/layout.txt
- name: Build shim
working-directory: shim
run: ./build.sh
- name: Run unit and integration tests
run: go test -count=1 -v ./...
env:
CGO_ENABLED: "0"
FFMPEG_SHIM_DIR: ${{ github.workspace }}/shim
# Test with FFmpeg (Windows amd64). Windows ARM64 has no public native
# GitHub-hosted runner, so it is covered by the compile matrix above.
test-with-ffmpeg-windows-amd64:
runs-on: windows-latest
name: Test (Windows amd64)
timeout-minutes: 45
env:
WINDOWS_FFMPEG_VERSION: "9.0.1"
WINDOWS_FFMPEG_SHA256: "6fd54b3b4f49117a307877b570f5e1659090f178973298658b41f5c559b5b5ab"
WINDOWS_FFMPEG_URL: "https://github.com/GyanD/codexffmpeg/releases/download/9.0.1/ffmpeg-9.0.1-full_build-shared.zip"
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install UCRT64 toolchain
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
make
mingw-w64-ucrt-x86_64-gcc
- name: Restore pinned FFmpeg build
id: ffmpeg-cache
uses: actions/cache/restore@v4
with:
path: .work/ffmpeg-windows-${{ env.WINDOWS_FFMPEG_VERSION }}
key: ffmpeg-windows-amd64-gyan-${{ env.WINDOWS_FFMPEG_VERSION }}-v1
- name: Install public shared FFmpeg ${{ env.WINDOWS_FFMPEG_VERSION }} build
if: steps.ffmpeg-cache.outputs.cache-hit != 'true'
shell: pwsh
run: |
$work = Join-Path $env:GITHUB_WORKSPACE ".work"
$archive = Join-Path $work "ffmpeg-$env:WINDOWS_FFMPEG_VERSION-full_build-shared.zip"
$expanded = Join-Path $work "ffmpeg-$env:WINDOWS_FFMPEG_VERSION-full_build-shared"
$prefix = Join-Path $work "ffmpeg-windows-$env:WINDOWS_FFMPEG_VERSION"
New-Item -ItemType Directory -Force -Path $work | Out-Null
curl.exe --fail --location --retry 3 --output $archive $env:WINDOWS_FFMPEG_URL
$actual = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
if ($actual -ne $env:WINDOWS_FFMPEG_SHA256) {
throw "FFmpeg archive checksum mismatch: $actual"
}
Expand-Archive -LiteralPath $archive -DestinationPath $work
Move-Item -LiteralPath $expanded -Destination $prefix
- name: Save pinned FFmpeg build
if: steps.ffmpeg-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: .work/ffmpeg-windows-${{ env.WINDOWS_FFMPEG_VERSION }}
key: ffmpeg-windows-amd64-gyan-${{ env.WINDOWS_FFMPEG_VERSION }}-v1
- name: Expose FFmpeg and shim to native Windows processes
shell: msys2 {0}
run: |
github_path="$(cygpath -u "$GITHUB_PATH")"
github_env="$(cygpath -u "$GITHUB_ENV")"
workspace="$(cygpath -u "$GITHUB_WORKSPACE")"
prefix="$workspace/.work/ffmpeg-windows-$WINDOWS_FFMPEG_VERSION"
printf '%s\n' "$(cygpath -w "$prefix/bin")" >> "$github_path"
printf '%s\n' "$(cygpath -w /ucrt64/bin)" >> "$github_path"
printf 'WINDOWS_FFMPEG_BIN=%s\n' "$(cygpath -w "$prefix/bin")" >> "$github_env"
printf 'FFMPEG_SHIM_DIR=%s\n' "$(cygpath -w "$workspace/shim")" >> "$github_env"
- name: Verify installed FFmpeg
shell: pwsh
run: |
$env:Path = "$env:WINDOWS_FFMPEG_BIN;$env:Path"
$version = ffmpeg -version | Select-Object -First 1
if ($version -notmatch "^ffmpeg version $env:WINDOWS_FFMPEG_VERSION(?:-| )") {
throw "Unexpected FFmpeg version: $version"
}
- name: Probe FFmpeg ABI layout
shell: msys2 {0}
run: |
workspace="$(cygpath -u "$GITHUB_WORKSPACE")"
prefix="$workspace/.work/ffmpeg-windows-$WINDOWS_FFMPEG_VERSION"
mkdir -p .work
gcc -std=c11 -Wall -Wextra -Werror \
-I"$prefix/include" \
internal/abi/testdata/layout_probe.c \
-o .work/layout-probe.exe
./.work/layout-probe.exe > .work/layout.txt
- name: Verify FFmpeg ABI layout
shell: pwsh
run: Get-Content .work\layout.txt | go run ./internal/abi/cmd/check-layout
- name: Build shim
shell: msys2 {0}
working-directory: shim
run: |
workspace="$(cygpath -u "$GITHUB_WORKSPACE")"
prefix="$workspace/.work/ffmpeg-windows-$WINDOWS_FFMPEG_VERSION"
make native \
CFLAGS_NATIVE="-Wall -Wextra -O2 -fPIC -DFFSHIM_HAVE_AVDEVICE=1 -I$prefix/include" \
LDFLAGS_NATIVE="-L$prefix/lib -lavdevice -lavformat -lavcodec -lavutil"
- name: Run unit and integration tests
shell: pwsh
run: |
$env:Path = "$env:WINDOWS_FFMPEG_BIN;$env:Path"
go test -count=1 -v ./...
env:
CGO_ENABLED: "0"
# Test with FFmpeg (macOS Intel)
test-with-ffmpeg-macos-intel:
runs-on: macos-15-intel
name: Test (macOS Intel)
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install FFmpeg
run: brew install ffmpeg
- name: Verify FFmpeg ABI layout
run: |
mkdir -p .work
cc -std=c11 -Wall -Wextra -Werror \
$(pkg-config --cflags libavutil libavcodec libavformat libavfilter libavdevice libswscale libswresample) \
internal/abi/testdata/layout_probe.c \
-o .work/layout-probe
.work/layout-probe | go run ./internal/abi/cmd/check-layout
- name: Build shim
working-directory: shim
run: ./build.sh
- name: Set library path
run: echo "DYLD_LIBRARY_PATH=${{ github.workspace }}/shim:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Run tests
run: go test -v ./...
env:
CGO_ENABLED: "0"
FFMPEG_SHIM_DIR: ${{ github.workspace }}/shim
- name: Run integration tests
run: go test -v -run Integration ./...
env:
CGO_ENABLED: "0"
FFMPEG_SHIM_DIR: ${{ github.workspace }}/shim
# Test with FFmpeg (macOS ARM64)
test-with-ffmpeg-macos-arm64:
runs-on: macos-15
name: Test (macOS ARM64)
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install FFmpeg
run: brew install ffmpeg
- name: Verify FFmpeg ABI layout
run: |
mkdir -p .work
cc -std=c11 -Wall -Wextra -Werror \
$(pkg-config --cflags libavutil libavcodec libavformat libavfilter libavdevice libswscale libswresample) \
internal/abi/testdata/layout_probe.c \
-o .work/layout-probe
.work/layout-probe | go run ./internal/abi/cmd/check-layout
- name: Build shim
working-directory: shim
run: ./build.sh
- name: Set library path
run: echo "DYLD_LIBRARY_PATH=${{ github.workspace }}/shim:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Run tests
run: go test -v ./...
env:
CGO_ENABLED: "0"
FFMPEG_SHIM_DIR: ${{ github.workspace }}/shim
- name: Run integration tests
run: go test -v -run Integration ./...
env:
CGO_ENABLED: "0"
FFMPEG_SHIM_DIR: ${{ github.workspace }}/shim
# Test graceful degradation without shim
test-degraded-mode:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Install FFmpeg (without building shim)
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg libavcodec-dev libavformat-dev libavutil-dev \
libswscale-dev libswresample-dev libavfilter-dev
- name: Run tests (no shim - degraded mode)
run: go test -v ./...
env:
CGO_ENABLED: "0"
# Explicitly unset shim directory to test degraded mode
FFMPEG_SHIM_DIR: ""
# Lint and vet
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: latest
args: --timeout=5m
- name: go vet
run: go vet ./...