diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ea2ec89f8..8f3deb706 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -16,8 +16,136 @@ jobs: # and its "pacman" format emits a PKGBUILD + source tarball rather than a # compiled pkg.tar.zst — both are upstream limitations. # ------------------------------------------------------------------ + # ------------------------------------------------------------------ + # Linux: one native package per distro, each built INSIDE that + # distro's container so the declared dependencies always resolve to + # the distro's own package names (dpkg-shlibdeps / rpmbuild + # auto-requires / Arch static base list). deb: hand-rolled dpkg-deb; + # rpm: rpmbuild; arch: makepkg. AppImage stays on the Ubuntu runner + # (self-contained by design). + # ------------------------------------------------------------------ linux: - name: Linux packages (deb, AppImage, pacman) + name: Linux packages (${{ matrix.distro }}) + runs-on: warp-ubuntu-latest-x64-8x + container: ${{ matrix.image }} + + strategy: + fail-fast: false + matrix: + include: + - distro: debian + image: debian:12 + - distro: fedora + image: fedora:41 + - distro: arch + image: archlinux:latest + + steps: + # git/curl must land BEFORE actions/checkout runs inside the + # container. + - name: Install git and fetch tools + run: | + case "${{ matrix.distro }}" in + debian) apt-get update && apt-get install -y git curl ;; + fedora) dnf install -y git curl ;; + arch) pacman -Sy --noconfirm git curl ;; + esac + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install Rust (stable) + uses: dtolnay/rust-toolchain@stable + + - name: Install system dependencies + run: | + case "${{ matrix.distro }}" in + debian) + apt-get install -y \ + build-essential cmake pkg-config nasm 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 \ + librsvg2-bin + ;; + fedora) + dnf install -y \ + gcc gcc-c++ cmake pkgconf-pkg-config nasm \ + 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 + ;; + arch) + pacman -S --needed --noconfirm \ + base-devel cmake pkgconf nasm \ + pipewire jack2 alsa-lib libpulse libsndfile \ + mesa vulkan-headers vulkan-icd-loader \ + libxkbcommon libxkbcommon-x11 librsvg + ;; + esac + + - name: Configure build environment + run: | + # OCIO builds from the ocio-sys vendored source (static) on + # Linux; the distro packages are too old for the bridge. + echo "OCIO_RS_ENABLE_REAL=1" >> "$GITHUB_ENV" + echo "OCIO_RS_LINK=static" >> "$GITHUB_ENV" + + - name: Cache cargo artifacts + uses: Swatinem/rust-cache@v2 + with: + shared-key: oak-${{ matrix.distro }} + + - name: Cache project FFmpeg + uses: actions/cache@v4 + with: + path: .cache/ffmpeg + key: ffmpeg-${{ matrix.distro }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }} + + - name: Build project FFmpeg (static, GPL + free codecs + hwaccel) + run: tooling/ffmpeg/build-ffmpeg.sh + + - name: Build (release) + run: cargo build --release --locked + + - name: Generate app icon (PNG from Oak_Icon.svg) + run: rsvg-convert -w 512 -h 512 Oak_Icon.svg -o icons/icon.png + + - name: Package + run: | + set -euo pipefail + VERSION="${GITHUB_REF_NAME#v}" + if [ "$VERSION" = "$GITHUB_REF_NAME" ]; then + VERSION="$(cargo pkgid | sed 's/.*#//' | cut -d@ -f2-)+git" + fi + case "${{ matrix.distro }}" in + debian) tooling/package/build-deb.sh "$VERSION" ;; + fedora) tooling/package/build-rpm.sh "$VERSION" ;; + arch) tooling/package/build-pkg.sh "$VERSION" ;; + esac + shell: bash + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: oak-linux-${{ matrix.distro }} + path: | + target/release/*.deb + target/release/*.rpm + target/release/*.pkg.tar.zst + if-no-files-found: error + + # ------------------------------------------------------------------ + # AppImage (self-contained; cargo-packager on the Ubuntu runner). + # ------------------------------------------------------------------ + appimage: + name: Linux AppImage runs-on: warp-ubuntu-latest-x64-8x steps: @@ -32,41 +160,31 @@ jobs: - name: Install system dependencies run: | tooling/install-deps.sh - # cmake/make for the vendored OpenColorIO build (ocio-sys - # `bundled`; Ubuntu's libopencolorio-dev is older than the - # bridge's API floor) + rsvg-convert for the app icon - # (cargo-packager needs PNG/icns/ico, the repo only has - # Oak_Icon.svg). sudo apt-get install -y \ - cmake \ + cmake librsvg2-bin \ libpipewire-0.3-dev libspa-0.2-dev libjack-jackd2-dev \ libasound2-dev libpulse-dev libsndfile1-dev \ - librsvg2-bin \ libgl1-mesa-dev libgl1-mesa-dri mesa-vulkan-drivers \ - libvulkan-dev libxkbcommon-dev + libvulkan-dev libxkbcommon-dev libxkbcommon-x11-dev - name: Configure build environment run: | - # No OCIO_INSTALL_DIR on purpose: the vendored OpenColorIO is - # built from source (see ci.yml). echo "OCIO_RS_ENABLE_REAL=1" >> "$GITHUB_ENV" echo "OCIO_RS_LINK=static" >> "$GITHUB_ENV" - name: Cache cargo artifacts uses: Swatinem/rust-cache@v2 with: - shared-key: oak-workspace + shared-key: oak-appimage - name: Cache project FFmpeg uses: actions/cache@v4 with: path: .cache/ffmpeg - key: ffmpeg-${{ runner.os }}-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }} + key: ffmpeg-appimage-${{ hashFiles('tooling/ffmpeg/build-ffmpeg.sh') }} - name: Build project FFmpeg (static, GPL + free codecs + hwaccel) - run: | - tooling/ffmpeg/build-ffmpeg.sh - echo "FFMPEG_DIR=$PWD/.cache/ffmpeg" >> "$GITHUB_ENV" + run: tooling/ffmpeg/build-ffmpeg.sh - name: Install cargo-packager run: cargo install cargo-packager --locked @@ -74,87 +192,22 @@ jobs: - name: Generate app icon (PNG from Oak_Icon.svg) run: | mkdir -p icons - # cargo-packager's tauri-icns 0.1.0 maps only 512x512@1x (and - # 1024x1024@2x); a plain 1024x1024 PNG aborts with "No matching - # IconType", so render 512x512. rsvg-convert -w 512 -h 512 Oak_Icon.svg -o icons/icon.png file icons/icon.png - # Build the packaged binaries (default members: the app, oak-cli, oak-worker). - name: Build (release) run: cargo build --release --locked - # Runtime-dependency audit: everything media-related (FFmpeg, OCIO) - # is statically linked, so the NEEDED list should contain only - # base-OS libraries (glibc, X11, ALSA/PipeWire, Vulkan) — names that - # are stable across Debian/openKylin (no distro-specific package - # names leak into the deb's dependency surface). - - name: Audit runtime dependencies - run: | - for bin in target/release/oak-editor target/release/oak-cli target/release/oak-worker; do - echo "== $bin" - objdump -p "$bin" | grep NEEDED || true - done + - name: Package (AppImage) + run: cargo packager --release --formats appimage - # Declare the FULL runtime dependency set on the deb. dpkg-shlibdeps - # resolves every NEEDED entry of every shipped binary to the exact - # package names of the build distro (FFmpeg/OCIO are statically - # linked, so only base-OS packages appear); an empty Depends is not - # self-containment, it just hides the requirements. Distros whose - # package names differ (openKylin) get their own build so the names - # always resolve. - - name: Declare full dependencies on the deb - run: | - set -euo pipefail - deps=$(for bin in target/release/oak-editor target/release/oak-cli target/release/oak-worker; do - dpkg-shlibdeps -O "$bin" - done | sed 's/^shlibs:Depends=//' | tr ',' '\n' | sed 's/^ //;s/ $//' | sort -u | paste -sd', ' -) - echo "declared deps: $deps" - for deb in target/release/*.deb; do - rm -rf .deb-repack && mkdir .deb-repack - dpkg-deb -R "$deb" .deb-repack - if grep -q '^Depends:' .deb-repack/DEBIAN/control; then - sed -i "s|^Depends:.*|Depends: $deps|" .deb-repack/DEBIAN/control - else - sed -i "1i Depends: $deps" .deb-repack/DEBIAN/control - fi - dpkg-deb -b .deb-repack "$deb" - rm -rf .deb-repack - done - - - - name: Package (deb, AppImage, pacman) - run: cargo packager --release --formats deb,appimage,pacman - - # cargo-packager has no rpm format; convert the deb with fpm. The gem - # bin dir may not be on PATH for the system ruby, so call fpm by path. - - name: Package (rpm, via fpm) - run: | - sudo apt-get install -y ruby ruby-dev rpm - sudo gem install --no-document fpm - sudo "$(sudo gem env gemdir)/bin/fpm" -s deb -t rpm --name oak-editor \ - --version "$(cargo pkgid | sed 's/.*#//' | cut -d@ -f2-)" \ - target/release/*.deb - mv ./*.rpm target/release/ - - - name: Upload artifacts + - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: oak-linux - path: | - target/release/*.deb - target/release/*.rpm - target/release/*.AppImage - target/release/*.tar.gz - target/release/PKGBUILD + name: oak-linux-appimage + path: target/release/*.AppImage if-no-files-found: error - # ------------------------------------------------------------------ - # macOS: Apple Silicon only. The app/CLI/worker call the module rlibs - # directly (oakengine is retired), so the .app bundle carries no dylib - # to fold in; package it with cargo-packager and create the DMG with - # hdiutil. - # ------------------------------------------------------------------ macos: name: macOS DMG (Apple Silicon) runs-on: warp-macos-15-arm64-6x @@ -337,7 +390,7 @@ jobs: # ------------------------------------------------------------------ release: name: Publish GitHub release - needs: [linux, macos, windows] + needs: [linux, appimage, macos, windows] if: startsWith(github.ref, 'refs/tags/v') runs-on: warp-ubuntu-latest-x64-8x steps: @@ -354,3 +407,4 @@ jobs: name: ${{ github.ref_name }} draft: false files: artifacts/* + diff --git a/packaging/oak.desktop b/packaging/oak.desktop new file mode 100644 index 000000000..2963031c7 --- /dev/null +++ b/packaging/oak.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=Oak Video Editor +Comment=A free, open-source non-linear video editor +Exec=oak-editor +Icon=oak +Categories=Video;AudioVideo; +Terminal=false diff --git a/tooling/package/PKGBUILD b/tooling/package/PKGBUILD new file mode 100644 index 000000000..384a9742e --- /dev/null +++ b/tooling/package/PKGBUILD @@ -0,0 +1,40 @@ +# Oak Video Editor - Non-Linear Video Editor +# Copyright (C) 2026 Oak Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Arch package build. The release binaries are built by the caller +# (tooling/package/build-pkg.sh) before makepkg runs; depends lists the +# base-OS packages the binaries link (FFmpeg/OCIO are statically linked). +pkgname=oak-editor +pkgver=@VERSION@ +pkgrel=1 +pkgdesc="Oak Video Editor — a free, open-source non-linear video editor" +arch=('x86_64') +url="https://github.com/OakVideoEditorCommunity/oak" +license=('GPL-3.0-or-later') +depends=('glibc' 'gcc-libs' 'alsa-lib' 'libpipewire' 'libx11' 'libxcb' + 'libxkbcommon' 'libxkbcommon-x11' 'libglvnd' 'vulkan-icd-loader' + 'fontconfig' 'freetype2') + +package() { + install -Dm755 "$OAK_BIN/oak-editor" "$pkgdir/usr/bin/oak-editor" + install -Dm755 "$OAK_BIN/oak-cli" "$pkgdir/usr/bin/oak-cli" + install -Dm755 "$OAK_BIN/oak-worker" "$pkgdir/usr/bin/oak-worker" + install -Dm644 "$OAK_ROOT/packaging/oak.desktop" "$pkgdir/usr/share/applications/oak.desktop" + install -Dm644 "$OAK_ROOT/icons/icon.png" "$pkgdir/usr/share/icons/hicolor/512x512/apps/oak.png" + for f in "$OAK_ROOT"/assets/i18n/*.yaml; do + install -Dm644 "$f" "$pkgdir/usr/share/oak/i18n/$(basename "$f")" + done +} diff --git a/tooling/package/oak.spec b/tooling/package/oak.spec new file mode 100644 index 000000000..379332bf2 --- /dev/null +++ b/tooling/package/oak.spec @@ -0,0 +1,44 @@ +# Oak Video Editor - Non-Linear Video Editor +# Copyright (C) 2026 Oak Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# RPM spec for Oak. Requires are AUTO-COMPUTED by rpmbuild from the +# packaged binaries' NEEDED entries (the distro's own names); do not add a +# static Requires list. +Name: oak-editor +Version: %{_version} +Release: 1%{?dist} +Summary: Oak Video Editor — a free, open-source non-linear video editor +License: GPL-3.0-or-later +URL: https://github.com/OakVideoEditorCommunity/oak + +%description +Oak is a non-linear video editor written in Rust (OpenFX plug-in host, +proxy editing, multicam, hardware decoding). + +%install +# Everything is staged into %{buildroot} by the caller (build-rpm.sh); +# nothing to compile here. +true + +%files +/usr/bin/oak-editor +/usr/bin/oak-cli +/usr/bin/oak-worker +/usr/share/applications/oak.desktop +/usr/share/icons/hicolor/512x512/apps/oak.png +/usr/share/oak/i18n/ + +%changelog