Files
oak-editor/.github/workflows/cd.yml
T
Mike-Solar 88a5bee50e ci/cd: unify both workflows on one seven-platform matrix
CI and CD now run the same seven environments in a single matrix:
Debian 12, Fedora 41, Arch and openKylin x64+arm64 in their distro
containers plus the macOS (12x) and Windows (32x) hosts, with identical
dependency lists and runner sizes, so a package a CD build needs cannot
be missing in CI. CD keeps building every package from scratch (no
vcpkg/cargo caches).

Fixes every failure the last CD run exposed:
- deb packaging: dpkg-shlibdeps needs a Debian source tree (give it a
  synthetic debian/control) and Debian 12/openKylin carry an older libva
  than FFmpeg 8 needs (vaMapBuffer2), so vcpkg's libva/libdrm ship next
  to the app with an $ORIGIN RUNPATH;
- AppImage: register the vcpkg libs with ldconfig so linuxdeploy finds
  libva-drm.so.2;
- Fedora: install perl-IPC-Cmd (vcpkg's openssl port requires it);
- macOS: cargo-packager produces "Oak Video Editor.app"; resolve the
  bundle instead of assuming "Oak.app";
- Windows: the vendored OCIO is compiled /MD, so drop +crt-static (the
  LNK2038 RuntimeLibrary mismatch) and bundle the MSVC runtime DLLs
  app-locally;
- containers: pin HOME for rustup and give WarpCache its token on the
  cache steps only (job-level env cannot reference the env context).
2026-09-24 17:32:53 +08:00

495 lines
21 KiB
YAML

name: CD
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write
# One matrix, seven platforms, the same environments CI tests in (see
# .github/workflows/ci.yml): Debian 12 / Fedora 41 / Arch / openKylin x64
# and arm64 containers plus the macOS and Windows hosts. Every package is
# built from scratch — no vcpkg/cargo caches: a restored vcpkg_installed
# or target tree has masked packaging problems before (stale ports,
# missing tools), and a release must not depend on restored state.
jobs:
package:
name: Package (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
# Container entries carry the container as JSON ({"image":...,
# "options":...}); the empty string means "run on the host"
# (actions/runner#265 allows an empty container value).
container: ${{ matrix.container != '' && fromJSON(matrix.container) || '' }}
# Cold vcpkg install + release build + packaging.
timeout-minutes: 150
strategy:
fail-fast: false
matrix:
include:
- name: Debian
platform: linux
distro: debian
arch: x64
runner: warp-ubuntu-latest-x64-32x
triplet: x64-linux
artifact: linux-debian
container: '{"image":"debian:12","options":"--shm-size=8g"}'
- name: Fedora
platform: linux
distro: fedora
arch: x64
runner: warp-ubuntu-latest-x64-32x
triplet: x64-linux
artifact: linux-fedora
container: '{"image":"fedora:41","options":"--shm-size=8g"}'
- name: Arch
platform: linux
distro: arch
arch: x64
runner: warp-ubuntu-latest-x64-32x
triplet: x64-linux
artifact: linux-arch
container: '{"image":"archlinux:latest","options":"--shm-size=8g"}'
- name: openKylin x64
platform: linux
distro: openkylin
arch: x64
runner: warp-ubuntu-latest-x64-32x
triplet: x64-linux
artifact: linux-openkylin-x64
container: '{"image":"openkylin/openkylin:latest","options":"--shm-size=8g"}'
- name: openKylin arm64
platform: linux
distro: openkylin
arch: arm64
runner: warp-ubuntu-latest-arm64-32x
triplet: arm64-linux
artifact: linux-openkylin-arm64
container: '{"image":"openkylin/openkylin:latest","options":"--shm-size=8g"}'
- name: macOS
platform: macos
distro: macos
arch: arm64
runner: warp-macos-26-arm64-12x
triplet: arm64-osx
artifact: macos
container: ''
- name: Windows
platform: windows
distro: windows
arch: x64
runner: warp-windows-2025-vs2026-x64-32x
triplet: x64-windows
artifact: windows
container: ''
steps:
# The container images are bare (Fedora/Arch even lack git);
# checkout and vcpkg need git/curl. First step of the job, so the
# package lists are still fresh.
- name: Bootstrap container (git, curl, wget)
if: matrix.container != ''
shell: bash
run: |
case "${{ matrix.distro }}" in
fedora) dnf install -y git curl wget which ;;
arch) pacman -Sy --noconfirm git curl wget which ;;
debian|openkylin) apt-get update && apt-get install -y git curl ca-certificates wget ;;
esac
- name: Checkout
uses: actions/checkout@v7
with:
# gpui/ is a git submodule; its crates are workspace members of
# their own repo and build as path dependencies of oakapp.
submodules: true
# Defender's real-time scanning slows the MSVC/vcpkg build down
# badly; disable it for the job and keep exclusions as the fallback
# when policy blocks the change.
- name: Disable Windows Defender scanning
if: matrix.platform == 'windows'
shell: pwsh
run: |
try {
Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction Stop
Set-MpPreference -DisableScriptScanning $true -ErrorAction SilentlyContinue
Set-MpPreference -DisableArchiveScanning $true -ErrorAction SilentlyContinue
Write-Host "Windows Defender real-time scanning disabled for this job"
} catch {
Write-Host "Windows Defender could not be disabled (non-fatal, falling back to exclusions): $_"
}
foreach ($path in @(
$env:GITHUB_WORKSPACE,
"$env:USERPROFILE\.cargo",
"$env:USERPROFILE\.rustup",
"$env:LOCALAPPDATA\vcpkg"
)) {
Add-MpPreference -ExclusionPath $path -ErrorAction SilentlyContinue
}
try {
Get-MpPreference |
Select-Object DisableRealtimeMonitoring, DisableScriptScanning, ExclusionPath |
Format-List
} catch {
Write-Host "Defender status unavailable: $_"
}
# The containers run as root but Actions sets HOME=/github/home;
# rustup refuses the euid mismatch ("$HOME differs from
# euid-obtained home directory") and would install a toolchain the
# later steps cannot find under the Actions home. Pin the job to
# root's home so rustup/cargo and the toolchain agree.
- name: Pin HOME for rustup
if: matrix.container != ''
shell: bash
run: |
{
echo "HOME=/root"
echo "CARGO_HOME=/root/.cargo"
echo "RUSTUP_HOME=/root/.rustup"
} >> "$GITHUB_ENV"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
# The Windows build is MSVC-ABI (the runner carries VS 2026):
# vcpkg's FFmpeg and the vendored OCIO build both want it.
toolchain: ${{ matrix.platform == 'windows' && 'stable-x86_64-pc-windows-msvc' || 'stable' }}
# ------------------------------------------------------------------
# System dependencies — one list per distro, byte-for-byte the same
# lists CI uses (see .github/workflows/ci.yml): what compiles there
# compiles here.
# ------------------------------------------------------------------
- name: Install system dependencies (Debian)
if: matrix.distro == 'debian'
shell: bash
run: |
apt-get update
apt-get install -y \
build-essential clang libclang-dev cmake pkg-config nasm \
git curl zip unzip tar python3 dpkg-dev \
libpipewire-0.3-dev libspa-0.2-dev libjack-jackd2-dev \
libasound2-dev libpulse-dev libsndfile1-dev \
libgl1-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers \
libvulkan-dev libxkbcommon-dev libxkbcommon-x11-dev xvfb libdrm-dev \
icc-profiles-free gdb file librsvg2-bin patchelf \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (Fedora)
if: matrix.distro == 'fedora'
shell: bash
run: |
dnf install -y \
gcc gcc-c++ clang clang-devel cmake pkgconf-pkg-config nasm \
git curl zip unzip tar python3 patch xz-utils which \
pipewire-devel jack-audio-connection-kit-devel \
alsa-lib-devel pulseaudio-libs-devel libsndfile-devel \
mesa-libGL-devel mesa-vulkan-drivers \
vulkan-headers vulkan-loader-devel \
libxkbcommon-devel libxkbcommon-x11-devel \
rpm-build librsvg2-tools libdrm-devel perl-IPC-Cmd \
xorg-x11-server-Xvfb xorg-x11-xauth gdb file \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (Arch)
if: matrix.distro == 'arch'
shell: bash
run: |
pacman -S --needed --noconfirm \
base-devel clang cmake pkgconf nasm \
git curl zip unzip tar python patch xz-utils which \
pipewire jack2 alsa-lib libpulse libsndfile \
mesa vulkan-headers vulkan-icd-loader \
libxkbcommon libxkbcommon-x11 librsvg libdrm \
xorg-server-xvfb xorg-xauth gdb file \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (openKylin)
if: matrix.distro == 'openkylin'
shell: bash
run: |
apt-get update
apt-get install -y \
build-essential clang libclang-dev cmake pkg-config nasm \
git curl zip unzip tar python3 patch xz-utils dpkg-dev \
libpipewire-0.3-dev libspa-0.2-dev libjack-jackd2-dev \
libasound2-dev libpulse-dev libsndfile1-dev \
libgl1-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers \
libvulkan-dev libxkbcommon-dev libxkbcommon-x11-dev xvfb libdrm-dev \
gdb file patchelf fonts-dejavu-core \
autoconf autoconf-archive automake libtool
- name: Install system dependencies (macOS)
if: matrix.platform == 'macos'
run: |
# Homebrew's pkgconf installs a `pkg-config` symlink, which is
# the name crates/oak-ffmpeg-link/build.rs invokes; nasm is what
# vcpkg's ffmpeg port requires to build (FFmpeg libraries come
# from the vcpkg manifest). librsvg stays for rsvg-convert (app
# icon) and is bundled into the .app by the dylib script.
brew install cmake pkg-config nasm librsvg autoconf automake libtool autoconf-archive
# ------------------------------------------------------------------
# vcpkg (manifest mode) — built from scratch, no caches
# ------------------------------------------------------------------
# Bootstrap a fresh clone rather than leaning on whatever vcpkg the
# image carries: `builtin-baseline`/`overrides` are only honored by
# a recent vcpkg-tool, and every platform must behave alike.
- name: Bootstrap vcpkg
if: matrix.platform != 'windows'
shell: bash
run: |
git clone https://github.com/microsoft/vcpkg.git .cache/vcpkg
.cache/vcpkg/bootstrap-vcpkg.sh -disableMetrics
echo "$PWD/.cache/vcpkg" >> "$GITHUB_PATH"
echo "VCPKG_ROOT=$PWD/.cache/vcpkg" >> "$GITHUB_ENV"
- name: Bootstrap vcpkg (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
git clone https://github.com/microsoft/vcpkg.git "$env:GITHUB_WORKSPACE\.cache\vcpkg"
& "$env:GITHUB_WORKSPACE\.cache\vcpkg\bootstrap-vcpkg.bat" -disableMetrics
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
"$env:GITHUB_WORKSPACE\.cache\vcpkg" >> $env:GITHUB_PATH
"VCPKG_ROOT=$env:GITHUB_WORKSPACE\.cache\vcpkg" >> $env:GITHUB_ENV
- name: Install dependencies (vcpkg manifest)
if: matrix.platform != 'windows'
shell: bash
run: |
# Source tarballs come from third-party hosts (x264 lives on
# code.videolan.org); a transient connection failure aborts the
# whole install — vcpkg refuses to retry that class of curl
# error — so retry here.
for attempt in 1 2 3; do
vcpkg install --triplet ${{ matrix.triplet }} && exit 0
echo "vcpkg install failed (attempt $attempt); retrying"
sleep 15
done
exit 1
- name: Install dependencies (vcpkg manifest, Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
for ($i = 1; $i -le 3; $i++) {
vcpkg install --triplet ${{ matrix.triplet }}
if ($LASTEXITCODE -eq 0) { exit 0 }
Write-Host "vcpkg install failed (attempt $i); retrying"
Start-Sleep -Seconds 15
}
exit 1
# ------------------------------------------------------------------
# Build environment
# ------------------------------------------------------------------
# ocio-sys builds a stub bridge unless these are set; the oak-core
# ocioutils tests need the real library.
# tooling/ocio-env.sh: vendored static OCIO (the [patch.crates-io]
# ocio-sys tracks shaloong/ocio-rs main, whose vendored sources build
# on GCC >= 16).
- name: Configure build environment (Linux)
if: matrix.platform == 'linux'
shell: bash
run: |
{
echo "CC=clang"
echo "CXX=clang++"
} >> "$GITHUB_ENV"
bash tooling/ocio-env.sh >> "$GITHUB_ENV"
prefix="$PWD/vcpkg_installed/${{ matrix.triplet }}"
echo "FFMPEG_DIR=$prefix" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$prefix/lib/pkgconfig" >> "$GITHUB_ENV"
echo "$prefix/tools/pkgconf" >> "$GITHUB_PATH"
echo "LD_LIBRARY_PATH=$prefix/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV"
# The Debian-family packaging tools resolve the ELF needs through
# ldd (dpkg-shlibdeps, linuxdeploy): register the vcpkg libs with
# the dynamic linker so `libva-drm.so.2` is found when a package
# is assembled.
echo "$prefix/lib" > /etc/ld.so.conf.d/oak-vcpkg.conf
ldconfig
- name: Configure build environment (macOS)
if: matrix.platform == 'macos'
shell: bash
run: |
# Vendored static OCIO (same as every non-Windows platform via
# tooling/ocio-env.sh); no OCIO_INSTALL_DIR override.
bash tooling/ocio-env.sh >> "$GITHUB_ENV"
prefix="$PWD/vcpkg_installed/${{ matrix.triplet }}"
echo "FFMPEG_DIR=$prefix" >> "$GITHUB_ENV"
echo "PKG_CONFIG_PATH=$prefix/lib/pkgconfig" >> "$GITHUB_ENV"
echo "$prefix/tools/pkgconf" >> "$GITHUB_PATH"
- name: Configure build environment (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
$prefix = "$env:GITHUB_WORKSPACE\vcpkg_installed\${{ matrix.triplet }}"
"FFMPEG_DIR=$prefix" >> $env:GITHUB_ENV
"PKG_CONFIG_PATH=$prefix\lib\pkgconfig" >> $env:GITHUB_ENV
"$prefix\tools\pkgconf" >> $env:GITHUB_PATH
# Bundled OCIO: ocio-sys' vendored sources build with the MSVC
# toolchain (what they need — the MSYS2 package was the
# workaround, not the preference), so no OCIO_INSTALL_DIR and
# no OCIO_RS_NO_MSVC_INCLUDES anywhere.
"OCIO_RS_ENABLE_REAL=1" >> $env:GITHUB_ENV
"OCIO_RS_LINK=static" >> $env:GITHUB_ENV
vcpkg list
- name: Install cargo-packager
if: matrix.distro == 'debian' || matrix.platform != 'linux'
run: cargo install cargo-packager --locked
- name: Generate app icon (PNG from Oak_Icon.svg)
if: matrix.platform != 'windows'
run: |
mkdir -p icons
if command -v rsvg-convert >/dev/null 2>&1; then
rsvg-convert -w 512 -h 512 Oak_Icon.svg -o icons/icon.png
else
# Defensive: some containers may not carry an SVG renderer;
# fall back to the committed 512x512 render.
cp assets/app-icon.png icons/icon.png
fi
# ------------------------------------------------------------------
# Build
# ------------------------------------------------------------------
# Default dynamic CRT on Windows: the vendored OCIO is compiled /MD,
# so forcing Rust to /MT fails with LNK2038 'RuntimeLibrary'
# mismatch; the redistributable DLLs ship with the installer below.
- name: Build (release)
run: cargo build --release --locked
# ------------------------------------------------------------------
# Package
# ------------------------------------------------------------------
- name: Package (Linux)
if: matrix.platform == 'linux'
shell: bash
run: |
set -euo pipefail
# The release version lives in [workspace.package] of the root
# Cargo.toml (single source of truth; tags do not carry it).
VERSION=$(sed -n '/^\[workspace\.package\]/,/^\[/s/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
case "${{ matrix.distro }}" in
debian)
# The general Debian-family package, labeled "+debian".
VCPKG_LIB="$PWD/vcpkg_installed/${{ matrix.triplet }}/lib" \
tooling/package/build-deb.sh "$VERSION" debian
# appimagetool self-extracts instead of mounting (containers
# have no FUSE).
APPIMAGE_EXTRACT_AND_RUN=1 cargo packager --release --formats appimage
;;
fedora)
tooling/package/build-rpm.sh "$VERSION"
;;
arch)
tooling/package/build-pkg.sh "$VERSION"
;;
openkylin)
# The openKylin build, labeled "+openkylin"; dpkg-shlibdeps
# resolves the runtime deps against openKylin's own repos
# and the vcpkg libva/libdrm ship next to the app (openKylin's
# system libva predates FFmpeg's vaMapBuffer2).
VCPKG_LIB="$PWD/vcpkg_installed/${{ matrix.triplet }}/lib" \
tooling/package/build-deb.sh "$VERSION" openkylin
;;
esac
- name: Package (macOS)
if: matrix.platform == 'macos'
run: |
cargo packager --release --formats app
# cargo-packager names the bundle after the packager
# `productName` ("Oak Video Editor.app"), so resolve it instead
# of guessing.
APP="$(ls -d target/release/*.app | head -1)"
tooling/package/bundle-dylibs-macos.sh "$APP"
rm -rf dmg-staging
mkdir -p dmg-staging
cp -R "$APP" dmg-staging/
ln -s /Applications dmg-staging/Applications
hdiutil create -volname "Oak Video Editor" \
-srcfolder dmg-staging -ov -format UDZO Oak-macOS-arm64.dmg
- name: Bundle runtime DLLs
if: matrix.platform == 'windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force target/pkg/win-dlls | Out-Null
# vcpkg's dynamic libs (FFmpeg + codecs).
Copy-Item "vcpkg_installed\${{ matrix.triplet }}\bin\*.dll" target/pkg/win-dlls/
# The MSVC runtime: the build keeps the default dynamic CRT (see
# the Build step), so ship the redistributable DLLs app-locally.
$crt = Get-ChildItem "$env:ProgramFiles\Microsoft Visual Studio\*\*\VC\Redist\MSVC\*\x64\Microsoft.VC*.CRT" -Directory -ErrorAction SilentlyContinue |
Sort-Object FullName | Select-Object -Last 1
if (-not $crt) { throw "MSVC CRT redist directory not found" }
Copy-Item "$($crt.FullName)\*.dll" target/pkg/win-dlls/
- name: Package (NSIS)
if: matrix.platform == 'windows'
shell: pwsh
run: cargo packager --release --formats nsis
# ------------------------------------------------------------------
# Upload
# ------------------------------------------------------------------
- name: Stage artifacts
if: matrix.platform != 'windows'
shell: bash
run: |
mkdir -p dist
cp target/release/*.deb dist/ 2>/dev/null || true
cp target/release/*.rpm dist/ 2>/dev/null || true
cp target/release/*.pkg.tar.zst dist/ 2>/dev/null || true
cp target/release/*.AppImage dist/ 2>/dev/null || true
cp ./*.dmg dist/ 2>/dev/null || true
ls -la dist
- name: Stage artifacts (Windows)
if: matrix.platform == 'windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force dist | Out-Null
Copy-Item target/release/*-setup.exe dist/
Get-ChildItem dist
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: oak-${{ matrix.artifact }}
path: dist/*
if-no-files-found: error
# ------------------------------------------------------------------
# Publish: attach every platform package to the v* tag's GitHub release
# (skipped on workflow_dispatch, which only uploads artifacts).
# ------------------------------------------------------------------
release:
name: Publish GitHub release
needs: [package]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: warp-ubuntu-latest-x64-32x
steps:
- name: Download all artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
merge-multiple: true
- name: Publish release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: false
files: artifacts/*