Bump version to 1.4.1, document supported architectures and RAW input #48
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # Keep these checksums in sync with ci.yml. | |
| YUNET_ORIGINAL_SHA256: 8f2383e4dd3cfbb4553ea8718107fc0423210dc964f9f4280604804ed2552fa4 | |
| YUNET_640_SHA256: d2c5a887db77802389261b13d2b9f5e1171b9d5787cb315d0add225812ff4d96 | |
| jobs: | |
| shader-validation: | |
| name: Validate WGSL shaders | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y pkg-config libdav1d-dev | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: 1.96.0 | |
| - name: Rust Cache | |
| uses: swatinem/rust-cache@v2 | |
| - name: Validate WGSL shaders | |
| run: cargo test -p fcs-utils validate_all_wgsl_files | |
| prepare-models: | |
| name: Prepare Release Models | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Download and process models | |
| shell: bash | |
| run: | | |
| mkdir -p models | |
| curl -L -o models/face_detection_yunet_2023mar.onnx https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx | |
| uvx --python 3.11 --with onnxsim==0.4.36 --with onnxruntime onnxsim models/face_detection_yunet_2023mar.onnx models/face_detection_yunet_2023mar_640.onnx --overwrite-input-shape "1,3,640,640" \ | |
| || uvx --python 3.11 --with onnxsim==0.4.36 --with onnxruntime onnxsim models/face_detection_yunet_2023mar.onnx models/face_detection_yunet_2023mar_640.onnx --input-shape "1,3,640,640" | |
| - name: Verify model checksums | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| import sys | |
| expected = { | |
| "models/face_detection_yunet_2023mar.onnx": os.environ["YUNET_ORIGINAL_SHA256"], | |
| "models/face_detection_yunet_2023mar_640.onnx": os.environ["YUNET_640_SHA256"], | |
| } | |
| def sha256(path: pathlib.Path) -> str: | |
| h = hashlib.sha256() | |
| with path.open("rb") as f: | |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): | |
| h.update(chunk) | |
| return h.hexdigest() | |
| failed = False | |
| for rel_path, expected_hash in expected.items(): | |
| path = pathlib.Path(rel_path) | |
| if not path.exists(): | |
| print(f"Missing model file: {rel_path}", file=sys.stderr) | |
| failed = True | |
| continue | |
| actual_hash = sha256(path) | |
| if actual_hash != expected_hash: | |
| print( | |
| f"Checksum mismatch for {rel_path}: expected {expected_hash}, got {actual_hash}", | |
| file=sys.stderr, | |
| ) | |
| failed = True | |
| else: | |
| print(f"Verified {rel_path}: {actual_hash}") | |
| if failed: | |
| sys.exit(1) | |
| PY | |
| - name: Upload prepared models | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-models | |
| path: | | |
| models/face_detection_yunet_2023mar.onnx | |
| models/face_detection_yunet_2023mar_640.onnx | |
| if-no-files-found: error | |
| retention-days: 1 | |
| windows-release: | |
| name: Windows Release Artifacts | |
| needs: [shader-validation, prepare-models] | |
| runs-on: windows-latest | |
| permissions: | |
| contents: write | |
| env: | |
| # Evaluated once at job start; used by signing steps to skip gracefully | |
| # when the certificate secret is not configured. | |
| HAS_CODE_SIGN: ${{ secrets.CODE_SIGN_PFX != '' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Download prepared models | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-models | |
| path: models | |
| - name: Verify model checksums | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| import sys | |
| expected = { | |
| "models/face_detection_yunet_2023mar.onnx": os.environ["YUNET_ORIGINAL_SHA256"], | |
| "models/face_detection_yunet_2023mar_640.onnx": os.environ["YUNET_640_SHA256"], | |
| } | |
| def sha256(path: pathlib.Path) -> str: | |
| h = hashlib.sha256() | |
| with path.open("rb") as f: | |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): | |
| h.update(chunk) | |
| return h.hexdigest() | |
| failed = False | |
| for rel_path, expected_hash in expected.items(): | |
| path = pathlib.Path(rel_path) | |
| if not path.exists(): | |
| print(f"Missing model file: {rel_path}", file=sys.stderr) | |
| failed = True | |
| continue | |
| actual_hash = sha256(path) | |
| if actual_hash != expected_hash: | |
| print( | |
| f"Checksum mismatch for {rel_path}: expected {expected_hash}, got {actual_hash}", | |
| file=sys.stderr, | |
| ) | |
| failed = True | |
| else: | |
| print(f"Verified {rel_path}: {actual_hash}") | |
| if failed: | |
| sys.exit(1) | |
| PY | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: 1.96.0 | |
| - name: Rust Cache | |
| uses: swatinem/rust-cache@v2 | |
| - name: Install NSIS | |
| shell: pwsh | |
| run: | | |
| choco install nsis --no-progress -y | |
| - name: Install WiX v7 | |
| shell: pwsh | |
| run: | | |
| dotnet tool install -g wix --version 7.0.0 | |
| wix -acceptEula wix7 extension add WixToolset.UI.wixext/7.0.0 | |
| - name: Install pkg-config | |
| shell: pwsh | |
| run: | | |
| choco install pkgconfiglite --no-progress -y | |
| - name: Cache vcpkg | |
| uses: actions/cache@v4 | |
| with: | |
| path: C:\vcpkg\installed | |
| key: vcpkg-dav1d-libheif-x64-windows-static-${{ runner.os }} | |
| - name: Install dav1d + libheif (vcpkg) | |
| shell: pwsh | |
| run: | | |
| if (-not (Test-Path "C:\vcpkg\vcpkg.exe")) { | |
| git clone https://github.com/microsoft/vcpkg C:\vcpkg | |
| & C:\vcpkg\bootstrap-vcpkg.bat | |
| } | |
| & C:\vcpkg\vcpkg.exe install dav1d:x64-windows-static libheif:x64-windows-static | |
| - name: Build release binaries | |
| env: | |
| RUSTFLAGS: -C target-feature=+crt-static | |
| PKG_CONFIG_PATH: C:\vcpkg\installed\x64-windows-static\lib\pkgconfig | |
| PKG_CONFIG_ALL_STATIC: "1" | |
| # libheif-sys finds libheif through the `vcpkg` crate, not pkg-config. | |
| VCPKG_ROOT: C:\vcpkg | |
| run: cargo build --release -p fcs-cli -p fcs-gui | |
| - name: Sign release binaries | |
| # Requires CODE_SIGN_PFX (base64-encoded PFX) and CODE_SIGN_PASSWORD | |
| # to be set as repository secrets. Skipped silently if absent. | |
| if: env.HAS_CODE_SIGN == 'true' | |
| shell: pwsh | |
| env: | |
| CODE_SIGN_PFX: ${{ secrets.CODE_SIGN_PFX }} | |
| CODE_SIGN_PASSWORD: ${{ secrets.CODE_SIGN_PASSWORD }} | |
| run: | | |
| $CertPath = "$env:TEMP\codesign.pfx" | |
| [IO.File]::WriteAllBytes($CertPath, [Convert]::FromBase64String($env:CODE_SIGN_PFX)) | |
| $SignTool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter signtool.exe | | |
| Sort-Object FullName -Descending | Select-Object -First 1).FullName | |
| if (-not $SignTool) { throw "signtool.exe not found" } | |
| foreach ($bin in @("target\release\fcs-cli.exe", "target\release\fcs-gui.exe")) { | |
| & $SignTool sign /fd SHA256 /f $CertPath /p $env:CODE_SIGN_PASSWORD ` | |
| /tr http://timestamp.digicert.com /td SHA256 ` | |
| /d "Face Crop Studio" $bin | |
| if ($LASTEXITCODE -ne 0) { throw "signtool failed for $bin" } | |
| } | |
| Remove-Item $CertPath -Force | |
| - name: Package artifacts | |
| shell: pwsh | |
| run: | | |
| $DistName = "face-crop-studio-windows-x86_64" | |
| $DistDir = "dist/$DistName" | |
| New-Item -ItemType Directory -Force -Path $DistDir | Out-Null | |
| New-Item -ItemType Directory -Force -Path "$DistDir/models" | Out-Null | |
| Copy-Item target/release/fcs-cli.exe "$DistDir/fcs-cli.exe" | |
| Copy-Item target/release/fcs-gui.exe "$DistDir/fcs-gui.exe" | |
| Copy-Item fcs-gui/assets/app_icon.ico "$DistDir/app_icon.ico" | |
| Copy-Item models/face_detection_yunet_2023mar_640.onnx "$DistDir/models/face_detection_yunet_2023mar_640.onnx" | |
| if (Test-Path samples) { | |
| Copy-Item -Recurse samples "$DistDir/samples" | |
| } | |
| if (Test-Path README.md) { | |
| Copy-Item README.md "$DistDir/README.md" | |
| } | |
| if (Test-Path LICENSE) { | |
| Copy-Item LICENSE "$DistDir/LICENSE" | |
| } | |
| if (Test-Path LICENSE-APACHE) { | |
| Copy-Item LICENSE-APACHE "$DistDir/LICENSE-APACHE" | |
| } | |
| if (Test-Path LICENSE-MIT) { | |
| Copy-Item LICENSE-MIT "$DistDir/LICENSE-MIT" | |
| } | |
| Compress-Archive -Path "$DistDir/*" -DestinationPath "dist/$DistName.zip" -Force | |
| - name: Verify packaged model path | |
| shell: pwsh | |
| run: | | |
| $DistName = "face-crop-studio-windows-x86_64" | |
| $ModelPath = "dist/$DistName/models/face_detection_yunet_2023mar_640.onnx" | |
| if (-not (Test-Path $ModelPath)) { | |
| Write-Error "Expected packaged model missing at $ModelPath" | |
| exit 1 | |
| } | |
| - name: Build NSIS installer | |
| shell: pwsh | |
| run: | | |
| $DistName = "face-crop-studio-windows-x86_64" | |
| $Version = "${{ github.ref_name }}" | |
| if ($Version.StartsWith("v")) { $Version = $Version.Substring(1) } | |
| $DistDir = Join-Path $PWD "dist\$DistName" | |
| $OutFile = Join-Path $PWD "dist\face-crop-studio-windows-x86_64-setup.exe" | |
| $Script = Join-Path $PWD "installer\windows\face-crop-studio.nsi" | |
| $env:Path = "C:\ProgramData\chocolatey\bin;$env:Path" | |
| $Makensis = (Get-Command makensis.exe -ErrorAction SilentlyContinue).Source | |
| if (-not $Makensis) { | |
| $Candidates = @( | |
| "$env:ProgramFiles(x86)\NSIS\makensis.exe", | |
| "$env:ProgramFiles\NSIS\makensis.exe", | |
| "C:\Program Files (x86)\NSIS\makensis.exe", | |
| "C:\Program Files\NSIS\makensis.exe" | |
| ) | |
| foreach ($Candidate in $Candidates) { | |
| if (Test-Path $Candidate) { | |
| $Makensis = $Candidate | |
| break | |
| } | |
| } | |
| } | |
| if (-not $Makensis) { | |
| throw "makensis.exe not found after NSIS installation" | |
| } | |
| & $Makensis /DAPP_VERSION=$Version /DDIST_DIR="$DistDir" /DOUT_FILE="$OutFile" "$Script" | |
| if ($LASTEXITCODE -ne 0) { | |
| throw "NSIS installer build failed with exit code $LASTEXITCODE" | |
| } | |
| if (-not (Test-Path $OutFile)) { | |
| throw "Installer missing at $OutFile" | |
| } | |
| - name: Build MSI installer | |
| shell: pwsh | |
| run: | | |
| $DistName = "face-crop-studio-windows-x86_64" | |
| $RawVersion = "${{ github.ref_name }}" | |
| if ($RawVersion.StartsWith("v")) { $RawVersion = $RawVersion.Substring(1) } | |
| $MsiVersion = $RawVersion.Split("-")[0] | |
| if ($MsiVersion -notmatch '^\d+\.\d+\.\d+$') { | |
| throw "Invalid MSI version '$MsiVersion' derived from tag '${{ github.ref_name }}'" | |
| } | |
| $DistDir = Join-Path $PWD "dist\$DistName" | |
| $MsiOut = Join-Path $PWD "dist\face-crop-studio-windows-x86_64.msi" | |
| Write-Host "Using MSI version: $MsiVersion" | |
| # WiX v7: single 'wix build' replaces heat + candle + light. | |
| # -arch x64 marks all harvested components as 64-bit (no XML patching needed). | |
| # <Files> in Product.wxs harvests SourceDir instead of the old heat.exe step. | |
| wix build ` | |
| -acceptEula wix7 ` | |
| -arch x64 ` | |
| -d SourceDir="$DistDir" ` | |
| -d MsiVersion="$MsiVersion" ` | |
| -ext WixToolset.UI.wixext ` | |
| -o "$MsiOut" ` | |
| installer\windows\wix\Product.wxs | |
| if ($LASTEXITCODE -ne 0) { throw "wix build failed with exit code $LASTEXITCODE" } | |
| if (-not (Test-Path $MsiOut)) { throw "MSI missing at $MsiOut" } | |
| - name: Generate checksums | |
| shell: bash | |
| run: | | |
| cd dist | |
| sha256sum face-crop-studio-windows-x86_64.zip > SHA256SUMS.txt | |
| sha256sum face-crop-studio-windows-x86_64-setup.exe >> SHA256SUMS.txt | |
| sha256sum face-crop-studio-windows-x86_64.msi >> SHA256SUMS.txt | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-release-artifacts | |
| path: | | |
| dist/face-crop-studio-windows-x86_64.zip | |
| dist/face-crop-studio-windows-x86_64-setup.exe | |
| dist/face-crop-studio-windows-x86_64.msi | |
| dist/SHA256SUMS.txt | |
| - name: Publish GitHub release assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| dist/face-crop-studio-windows-x86_64.zip | |
| dist/face-crop-studio-windows-x86_64-setup.exe | |
| dist/face-crop-studio-windows-x86_64.msi | |
| dist/SHA256SUMS.txt | |
| macos-release: | |
| name: macOS Release Artifacts (Apple Silicon) | |
| needs: [shader-validation, prepare-models] | |
| runs-on: macos-latest | |
| permissions: | |
| contents: write | |
| env: | |
| # Evaluated once at job start so signing/notarization steps can skip | |
| # gracefully when the Apple secrets aren't configured. | |
| HAS_APPLE_SIGNING: ${{ secrets[format('{0}', 'APPLE_DEVELOPER_ID_CERT')] != '' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Download prepared models | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-models | |
| path: models | |
| - name: Verify model checksums | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| import sys | |
| expected = { | |
| "models/face_detection_yunet_2023mar.onnx": os.environ["YUNET_ORIGINAL_SHA256"], | |
| "models/face_detection_yunet_2023mar_640.onnx": os.environ["YUNET_640_SHA256"], | |
| } | |
| def sha256(path: pathlib.Path) -> str: | |
| h = hashlib.sha256() | |
| with path.open("rb") as f: | |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): | |
| h.update(chunk) | |
| return h.hexdigest() | |
| failed = False | |
| for rel_path, expected_hash in expected.items(): | |
| path = pathlib.Path(rel_path) | |
| if not path.exists(): | |
| print(f"Missing model file: {rel_path}", file=sys.stderr) | |
| failed = True | |
| continue | |
| actual_hash = sha256(path) | |
| if actual_hash != expected_hash: | |
| print( | |
| f"Checksum mismatch for {rel_path}: expected {expected_hash}, got {actual_hash}", | |
| file=sys.stderr, | |
| ) | |
| failed = True | |
| else: | |
| print(f"Verified {rel_path}: {actual_hash}") | |
| if failed: | |
| sys.exit(1) | |
| PY | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: 1.96.0 | |
| targets: aarch64-apple-darwin | |
| - name: Rust Cache | |
| uses: swatinem/rust-cache@v2 | |
| - name: Install build dependencies (Homebrew) | |
| run: | | |
| brew update | |
| # dav1d + pkg-config: AVIF decode support (mirrors the Windows vcpkg setup). | |
| # libheif: HEIC/HEIF decode (brew ships a recent libheif for libheif-rs). | |
| # librsvg: rsvg-convert for SVG → PNG iconset generation. | |
| # create-dmg: drag-to-Applications DMG layout. | |
| brew install dav1d libheif pkg-config librsvg create-dmg | |
| - name: Build release binary (aarch64-apple-darwin) | |
| run: cargo build --release -p fcs-gui --target aarch64-apple-darwin | |
| - name: Import Developer ID certificate | |
| # Mirrors the Windows signing pattern: only runs if the cert secret is | |
| # configured. Imports the .p12 into a temporary keychain so codesign | |
| # picks it up, then the build script uses APPLE_DEVELOPER_ID_NAME to | |
| # select the identity. | |
| if: env.HAS_APPLE_SIGNING == 'true' | |
| env: | |
| APPLE_DEVELOPER_ID_CERT: ${{ secrets[format('{0}', 'APPLE_DEVELOPER_ID_CERT')] }} | |
| APPLE_DEVELOPER_ID_PASS: ${{ secrets[format('{0}', 'APPLE_DEVELOPER_ID_PASS')] }} | |
| KEYCHAIN_PASS: ${{ github.run_id }}-keychain | |
| run: | | |
| CERT_PATH="$RUNNER_TEMP/certificate.p12" | |
| KEYCHAIN_PATH="$RUNNER_TEMP/codesign.keychain-db" | |
| echo -n "$APPLE_DEVELOPER_ID_CERT" | base64 --decode > "$CERT_PATH" | |
| security create-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" | |
| security unlock-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| security import "$CERT_PATH" -P "$APPLE_DEVELOPER_ID_PASS" \ | |
| -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH" | |
| security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASS" "$KEYCHAIN_PATH" | |
| security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"') | |
| rm -f "$CERT_PATH" | |
| - name: Build .app bundle and DMG | |
| env: | |
| APPLE_DEVELOPER_ID_NAME: ${{ secrets[format('{0}', 'APPLE_DEVELOPER_ID_NAME')] }} | |
| APPLE_NOTARIZE_USER: ${{ secrets[format('{0}', 'APPLE_NOTARIZE_USER')] }} | |
| APPLE_NOTARIZE_PASS: ${{ secrets[format('{0}', 'APPLE_NOTARIZE_PASS')] }} | |
| APPLE_TEAM_ID: ${{ secrets[format('{0}', 'APPLE_TEAM_ID')] }} | |
| run: | | |
| RawVersion="${{ github.ref_name }}" | |
| Version="${RawVersion#v}" | |
| bash installer/macos/build_macos.sh "$Version" | |
| - name: Generate checksums | |
| shell: bash | |
| run: | | |
| cd dist/macos | |
| shasum -a 256 *.dmg > SHA256SUMS.txt | |
| cat SHA256SUMS.txt | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: macos-release-artifacts | |
| path: | | |
| dist/macos/*.dmg | |
| dist/macos/SHA256SUMS.txt | |
| - name: Publish GitHub release assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| dist/macos/*.dmg | |
| dist/macos/SHA256SUMS.txt | |
| linux-release: | |
| name: Linux Release Artifacts (x86_64) | |
| needs: [shader-validation, prepare-models] | |
| # Build on 22.04 for broad glibc compatibility (binaries link against | |
| # glibc 2.35 and run on Ubuntu 22.04+ and other modern distros). | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Download prepared models | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-models | |
| path: models | |
| - name: Verify model checksums | |
| shell: bash | |
| run: | | |
| python - <<'PY' | |
| import hashlib | |
| import os | |
| import pathlib | |
| import sys | |
| expected = { | |
| "models/face_detection_yunet_2023mar.onnx": os.environ["YUNET_ORIGINAL_SHA256"], | |
| "models/face_detection_yunet_2023mar_640.onnx": os.environ["YUNET_640_SHA256"], | |
| } | |
| def sha256(path: pathlib.Path) -> str: | |
| h = hashlib.sha256() | |
| with path.open("rb") as f: | |
| for chunk in iter(lambda: f.read(1024 * 1024), b""): | |
| h.update(chunk) | |
| return h.hexdigest() | |
| failed = False | |
| for rel_path, expected_hash in expected.items(): | |
| path = pathlib.Path(rel_path) | |
| if not path.exists(): | |
| print(f"Missing model file: {rel_path}", file=sys.stderr) | |
| failed = True | |
| continue | |
| actual_hash = sha256(path) | |
| if actual_hash != expected_hash: | |
| print( | |
| f"Checksum mismatch for {rel_path}: expected {expected_hash}, got {actual_hash}", | |
| file=sys.stderr, | |
| ) | |
| failed = True | |
| else: | |
| print(f"Verified {rel_path}: {actual_hash}") | |
| if failed: | |
| sys.exit(1) | |
| PY | |
| - name: Install build dependencies (apt) | |
| run: | | |
| DAV1D_VERSION=1.5.1 | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| meson \ | |
| ninja-build \ | |
| nasm \ | |
| pkg-config \ | |
| libdav1d-dev \ | |
| librsvg2-bin \ | |
| libgtk-3-dev \ | |
| libxkbcommon-dev \ | |
| libwayland-dev \ | |
| libxcb1-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-render0-dev \ | |
| libegl-dev \ | |
| libgl-dev | |
| curl -fsSL -o /tmp/dav1d.tar.xz \ | |
| "https://download.videolan.org/pub/videolan/dav1d/${DAV1D_VERSION}/dav1d-${DAV1D_VERSION}.tar.xz" | |
| tar -xf /tmp/dav1d.tar.xz -C /tmp | |
| meson setup "/tmp/dav1d-${DAV1D_VERSION}/build" "/tmp/dav1d-${DAV1D_VERSION}" \ | |
| --prefix=/usr/local \ | |
| --libdir=lib \ | |
| --buildtype=release \ | |
| --default-library=static \ | |
| -Denable_tools=false \ | |
| -Denable_tests=false | |
| ninja -C "/tmp/dav1d-${DAV1D_VERSION}/build" | |
| sudo ninja -C "/tmp/dav1d-${DAV1D_VERSION}/build" install | |
| export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH:-}" | |
| echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> "$GITHUB_ENV" | |
| echo "PKG_CONFIG_ALL_STATIC=1" >> "$GITHUB_ENV" | |
| pkg-config --modversion dav1d | |
| # HEIC/HEIF decode: apt's libheif (1.12 on 22.04) is older than the 1.21 | |
| # libheif-rs needs, so build libde265 (HEVC decode) + libheif from source as | |
| # static libs, mirroring the dav1d-from-source approach for clean static linking. | |
| DE265_VERSION=1.0.15 | |
| LIBHEIF_VERSION=1.23.0 | |
| curl -fsSL -o /tmp/libde265.tar.gz \ | |
| "https://github.com/strukturag/libde265/releases/download/v${DE265_VERSION}/libde265-${DE265_VERSION}.tar.gz" | |
| tar -xf /tmp/libde265.tar.gz -C /tmp | |
| cmake -S "/tmp/libde265-${DE265_VERSION}" -B "/tmp/libde265-${DE265_VERSION}/build" \ | |
| -DCMAKE_INSTALL_PREFIX=/usr/local \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DENABLE_DECODER=ON \ | |
| -DENABLE_ENCODER=OFF \ | |
| -DENABLE_SDL=OFF | |
| cmake --build "/tmp/libde265-${DE265_VERSION}/build" --parallel | |
| sudo cmake --install "/tmp/libde265-${DE265_VERSION}/build" | |
| curl -fsSL -o /tmp/libheif.tar.gz \ | |
| "https://github.com/strukturag/libheif/releases/download/v${LIBHEIF_VERSION}/libheif-${LIBHEIF_VERSION}.tar.gz" | |
| tar -xf /tmp/libheif.tar.gz -C /tmp | |
| cmake -S "/tmp/libheif-${LIBHEIF_VERSION}" -B "/tmp/libheif-${LIBHEIF_VERSION}/build" \ | |
| -DCMAKE_INSTALL_PREFIX=/usr/local \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DWITH_EXAMPLES=OFF \ | |
| -DWITH_LIBDE265=ON \ | |
| -DWITH_X265=OFF \ | |
| -DWITH_AOM_ENCODER=OFF \ | |
| -DWITH_AOM_DECODER=OFF \ | |
| -DENABLE_PLUGIN_LOADING=OFF | |
| cmake --build "/tmp/libheif-${LIBHEIF_VERSION}/build" --parallel | |
| sudo cmake --install "/tmp/libheif-${LIBHEIF_VERSION}/build" | |
| pkg-config --modversion libheif | |
| pkg-config --libs --static libheif | |
| echo "SYSTEM_DEPS_LIBHEIF_LINK=static" >> "$GITHUB_ENV" | |
| echo "SYSTEM_DEPS_LIBHEIF_SEARCH_NATIVE=/usr/local/lib" >> "$GITHUB_ENV" | |
| echo "SYSTEM_DEPS_LIBHEIF_LIB=heif de265" >> "$GITHUB_ENV" | |
| echo "SYSTEM_DEPS_LIBHEIF_INCLUDE=/usr/local/include" >> "$GITHUB_ENV" | |
| echo "RUSTFLAGS=-l dylib=stdc++" >> "$GITHUB_ENV" | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: 1.96.0 | |
| - name: Rust Cache | |
| uses: swatinem/rust-cache@v2 | |
| - name: Install cargo-deb | |
| run: | | |
| cargo install cargo-deb --locked --version 3.7.0 | |
| cargo deb --version | |
| - name: Install appimagetool | |
| # Extract instead of running directly so we don't need FUSE on the runner. | |
| run: | | |
| curl -fsSL -o /tmp/appimagetool.AppImage \ | |
| https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage | |
| chmod +x /tmp/appimagetool.AppImage | |
| (cd /tmp && ./appimagetool.AppImage --appimage-extract >/dev/null) | |
| sudo mv /tmp/squashfs-root /opt/appimagetool | |
| sudo ln -sf /opt/appimagetool/AppRun /usr/local/bin/appimagetool | |
| - name: Build release binary | |
| run: cargo build --release -p fcs-gui | |
| - name: Build AppImage and .deb | |
| run: | | |
| RawVersion="${{ github.ref_name }}" | |
| Version="${RawVersion#v}" | |
| bash installer/linux/build_linux.sh "$Version" | |
| ls -lh dist/linux | |
| - name: Generate checksums | |
| shell: bash | |
| run: | | |
| cd dist/linux | |
| sha256sum *.AppImage *.deb > SHA256SUMS.txt | |
| cat SHA256SUMS.txt | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-release-artifacts | |
| path: | | |
| dist/linux/*.AppImage | |
| dist/linux/*.deb | |
| dist/linux/SHA256SUMS.txt | |
| - name: Publish GitHub release assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| dist/linux/*.AppImage | |
| dist/linux/*.deb | |
| dist/linux/SHA256SUMS.txt |