Skip to content

build-ffmpeg

build-ffmpeg #55

Workflow file for this run

name: build-ffmpeg
on:
workflow_dispatch:
inputs:
profile:
description: Path to build profile
required: false
default: profiles/default.yml
type: string
ffmpeg_version:
description: Override FFmpeg version (optional)
required: false
default: ""
type: string
nonfree:
description: Enable nonfree (requires nonfree libs configured)
required: false
default: false
type: boolean
macos_arm64:
description: Build macOS arm64
required: false
default: true
type: boolean
macos_x86_64:
description: Build macOS x86_64 (Intel)
required: false
default: false
type: boolean
linux_x86_64:
description: Build Linux x86_64
required: false
default: false
type: boolean
windows_x86_64:
description: Build Windows x86_64
required: false
default: false
type: boolean
push:
tags:
- "v*"
jobs:
setup:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
ffmpeg_version: ${{ steps.set-matrix.outputs.ffmpeg_version }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
shell: bash
run: |
# On tag push, build the default release platforms (macOS arm64 + Windows x86_64).
# On workflow_dispatch, use the selected inputs.
IS_RELEASE=${{ github.event_name != 'workflow_dispatch' }}
# Resolve FFmpeg version from input or profile
FFMPEG_VERSION="${{ inputs.ffmpeg_version }}"
if [[ -z "$FFMPEG_VERSION" ]]; then
FFMPEG_VERSION=$(yq '.ffmpeg.version' profiles/default.yml)
fi
echo "ffmpeg_version=$FFMPEG_VERSION" >> $GITHUB_OUTPUT
ENTRIES='[]'
add() { ENTRIES=$(echo "$ENTRIES" | jq -c --argjson e "$1" '. + [$e]'); }
if [[ "$IS_RELEASE" == "true" ]] || [[ "${{ inputs.macos_arm64 }}" == "true" ]]; then
add '{"os":"macos-26","arch":"arm64","runner":"macos-26"}'
fi
if [[ "$IS_RELEASE" == "true" ]] || [[ "${{ inputs.macos_x86_64 }}" == "true" ]]; then
add '{"os":"macos-26-intel","arch":"x86_64","runner":"macos-26-intel"}'
fi
if [[ "${{ inputs.linux_x86_64 }}" == "true" ]]; then
add '{"os":"ubuntu-22.04","arch":"x86_64","runner":"ubuntu-22.04"}'
fi
if [[ "$IS_RELEASE" == "true" ]] || [[ "${{ inputs.windows_x86_64 }}" == "true" ]]; then
add '{"os":"windows-2022","arch":"x86_64","runner":"windows-2022"}'
fi
echo "matrix={\"include\":$ENTRIES}" >> $GITHUB_OUTPUT
build:
needs: setup
name: ${{ matrix.os }} • ${{ matrix.arch }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
env:
# Use workflow_dispatch inputs when present, otherwise fall back to defaults
PROFILE: >-
${{ github.event_name == 'workflow_dispatch' && github.event.inputs.profile || 'profiles/default.yml' }}
FFMPEG_VERSION: ${{ needs.setup.outputs.ffmpeg_version }}
ENABLE_NONFREE: >-
${{ github.event_name == 'workflow_dispatch' && github.event.inputs.nonfree || false }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
BUILD_CACHE: ${{ github.workspace }}/.build-cache
# Exposed for use in step `if` conditions — empty string when secret is not set
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
steps:
- uses: actions/checkout@v4
- name: Restore caches
uses: actions/cache@v4
with:
path: |
${{ env.CCACHE_DIR }}
${{ env.BUILD_CACHE }}
# Cache key based on OS/arch + all profiles
key: ${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('profiles/**/*.yml') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-
# Signing and notarization are optional — skipped automatically if secrets are not set.
# Required to run on macOS without a Gatekeeper warning. See README for setup instructions.
- name: Import Apple certificate (macOS)
if: runner.os == 'macOS' && env.APPLE_CERTIFICATE != ''
env:
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
rm certificate.p12
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | head -n 1)
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> $GITHUB_ENV
- name: Bootstrap toolchain (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
if [[ "${{ runner.os }}" == "Linux" ]]; then
./scripts/bootstrap-linux.sh
else
./scripts/bootstrap-macos.sh
fi
- name: Bootstrap toolchain (Windows)
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >-
base-devel git nasm yasm zip autoconf automake libtool
mingw-w64-x86_64-toolchain mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-meson
- name: Build FFmpeg (Linux/macOS)
if: runner.os != 'Windows'
shell: bash
run: ./scripts/build-ffmpeg.sh
- name: Upload FFmpeg config.log on failure (macOS/Linux)
if: runner.os != 'Windows' && failure()
uses: actions/upload-artifact@v4
with:
name: ffmpeg-config-log-${{ matrix.os }}-${{ matrix.arch }}
path: .build-cache/src/ffmpeg-*/ffbuild/config.log
if-no-files-found: ignore
- name: Build FFmpeg (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: ./scripts/build-ffmpeg-windows.sh
- name: Sign binaries (macOS)
if: runner.os == 'macOS' && env.APPLE_CERTIFICATE != ''
run: |
set -euo pipefail
for bin in out/*/bin/ffmpeg out/*/bin/ffprobe .build-cache/out/*/bin/ffmpeg .build-cache/out/*/bin/ffprobe; do
[ -x "$bin" ] || continue
codesign --force --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$bin"
echo "Signed: $bin"
done
- name: Package artifacts (POSIX)
if: runner.os != 'Windows'
shell: bash
run: ./scripts/package.sh
- name: Package artifacts (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: ./scripts/package-windows.sh
- name: Notarize (macOS)
if: runner.os == 'macOS' && env.APPLE_CERTIFICATE != ''
env:
APPLE_API_KEY_CONTENTS: ${{ secrets.APPLE_API_KEY_CONTENTS }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
run: |
set -euo pipefail
echo "$APPLE_API_KEY_CONTENTS" | base64 --decode > AuthKey.p8
for zip in dist/*.zip; do
echo "==> Notarizing $zip"
xcrun notarytool submit "$zip" \
--key AuthKey.p8 \
--key-id "$APPLE_API_KEY" \
--issuer "$APPLE_API_ISSUER" \
--wait
done
rm AuthKey.p8
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ffmpeg-${{ matrix.os }}-${{ matrix.arch }}${{ env.ENABLE_NONFREE == 'true' && '-nonfree' || '' }}
path: dist/*.zip
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: ./dist
- uses: softprops/action-gh-release@v2
with:
files: dist/**/**.zip
generate_release_notes: true