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 43 / 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:43","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 --setopt=install_weak_deps=False --setopt=max_parallel_downloads=16 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: | # Fedora 41 is EOL (its mirrors moved to the slow archive), so # the matrix uses the current release; weak dependencies (docs, # fonts, optional tooling) are skipped and downloads run wide. dnf install -y --setopt=install_weak_deps=False \ --setopt=max_parallel_downloads=16 \ gcc gcc-c++ clang clang-devel cmake pkgconf-pkg-config nasm \ git curl zip unzip tar python3 patch xz 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 perl-FindBin perl-File-Basename perl-File-Compare \ perl-File-Copy perl-File-Path perl-File-Temp perl-Time-Piece \ 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 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 }} --overlay-triplets tooling/vcpkg-triplets/release && 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 }} --overlay-triplets tooling/vcpkg-triplets/release 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/*