From 47a5bf8477cd8d1d1b55199667df21ea636938e9 Mon Sep 17 00:00:00 2001 From: Stuart Cameron Date: Mon, 5 Oct 2026 22:08:23 +1100 Subject: [PATCH 1/2] feat(linux): ship an AppImage, with the tarball as the fallback The Linux release was a bare tarball: extract it and run ./vapourbox, with no desktop entry, no icon and no update path. package-linux.sh now also builds an AppImage and its .zsync from the same tree. - AppRun exports nothing, so no environment leaks into the worker or the ffmpeg/vspipe it spawns from the downloaded deps bundle. - Deps stay a first-launch download under $XDG_DATA_HOME: the mount is read-only, and deps are versioned separately in two CPU tiers. - Update information points at the latest GitHub release's .zsync. A missing zsyncmake fails the build rather than embedding a dead pointer. - appimagetool is pinned to 1.9.1 by version and SHA-256. - packaging_test.dart lints the update filename, the release upload lists and the desktop/icon naming against APPLICATION_ID. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014GLXdGLfPwgYjW1AkonGqN --- .github/workflows/build-linux.yml | 18 ++- CLAUDE.md | 37 ++++- README.md | 14 +- Scripts/ci-build-and-release.sh | 4 +- Scripts/generate-app-icons.sh | 6 + Scripts/package-linux.sh | 151 ++++++++++++++++-- Scripts/release.sh | 8 +- app/test/packaging_test.dart | 57 +++++++ docs/BUILDING.md | 13 +- packaging/linux/AppRun | 10 ++ .../linux/app.vapourbox.VapourBox.desktop | 12 ++ packaging/linux/app.vapourbox.VapourBox.png | Bin 0 -> 72389 bytes 12 files changed, 301 insertions(+), 29 deletions(-) create mode 100755 packaging/linux/AppRun create mode 100644 packaging/linux/app.vapourbox.VapourBox.desktop create mode 100644 packaging/linux/app.vapourbox.VapourBox.png diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 554da86e..74925cd7 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -35,6 +35,9 @@ jobs: sudo apt-get update sudo apt-get install -y clang cmake git ninja-build pkg-config \ libgtk-3-dev liblzma-dev libstdc++-12-dev + # zsyncmake writes the .zsync the AppImage's update information names; + # desktop-file-validate lets appimagetool check the desktop entry. + sudo apt-get install -y zsync desktop-file-utils - name: Setup Flutter uses: subosito/flutter-action@v2 @@ -113,7 +116,11 @@ jobs: uses: actions/upload-artifact@v5 with: name: VapourBox-${{ inputs.version }}-linux-x64 - path: dist/VapourBox-${{ inputs.version }}-linux-x64.tar.gz + if-no-files-found: error + path: | + dist/VapourBox-${{ inputs.version }}-linux-x64.AppImage + dist/VapourBox-${{ inputs.version }}-linux-x64.AppImage.zsync + dist/VapourBox-${{ inputs.version }}-linux-x64.tar.gz build-arm64: if: ${{ inputs.arch == 'both' || inputs.arch == 'arm64' }} @@ -128,6 +135,9 @@ jobs: sudo apt-get update sudo apt-get install -y clang cmake git ninja-build pkg-config \ libgtk-3-dev liblzma-dev libstdc++-12-dev + # zsyncmake writes the .zsync the AppImage's update information names; + # desktop-file-validate lets appimagetool check the desktop entry. + sudo apt-get install -y zsync desktop-file-utils - name: Setup Flutter run: | @@ -211,4 +221,8 @@ jobs: uses: actions/upload-artifact@v5 with: name: VapourBox-${{ inputs.version }}-linux-arm64 - path: dist/VapourBox-${{ inputs.version }}-linux-arm64.tar.gz + if-no-files-found: error + path: | + dist/VapourBox-${{ inputs.version }}-linux-arm64.AppImage + dist/VapourBox-${{ inputs.version }}-linux-arm64.AppImage.zsync + dist/VapourBox-${{ inputs.version }}-linux-arm64.tar.gz diff --git a/CLAUDE.md b/CLAUDE.md index aeb410bb..0deed686 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -266,8 +266,8 @@ dart run build_runner build # Windows .\Scripts\package-windows.ps1 -Version "X.Y.Z" [-SkipBuild] -# Linux -./Scripts/package-linux.sh --version X.Y.Z [--skip-build] +# Linux — writes an AppImage (+ its .zsync) and a tarball of the same tree +./Scripts/package-linux.sh --version X.Y.Z [--skip-build] [--skip-appimage] # Test packaged app from terminal to see errors: # macOS: @@ -397,7 +397,8 @@ Adding a filter touches many files. Missing any step causes silent failures (fil icon with CoreGraphics at whatever size you ask for, so no ImageMagick or librsvg is needed (and every size is drawn at its own scale rather than downsampled from one bitmap). `Scripts/generate-app-icons.sh` drives it at the -sizes each platform wants and packs the Windows `.ico` in pure Python: +sizes each platform wants, packs the Windows `.ico` in pure Python, and writes +the Linux AppImage icon (`packaging/linux/app.vapourbox.VapourBox.png`): ```bash ./Scripts/generate-app-icons.sh # macOS only; needs Swift from the Xcode CLT @@ -1481,6 +1482,34 @@ placement and a version skew would change chroma per-OS. - Show in Folder: `xdg-open ` - Debug builds: use `./Scripts/run-debug-linux.sh` - No code signing or quarantine removal needed +- **Ships as an AppImage, with a tarball of the same tree as the fallback** + (`package-linux.sh`; pieces in `packaging/linux/`). The rules that keep it + working: + - **`AppRun` exports nothing** — no `LD_LIBRARY_PATH`, no bundled GTK. Its + environment is inherited by the worker and by the ffmpeg/vspipe it spawns + from the downloaded deps bundle, so anything set there leaks into them. + - **The mount is read-only** (`/tmp/.mount_*`). Nothing may be written next + to the executable on Linux; deps and add-ons already live under + `$XDG_DATA_HOME`, which is also why deps stay a first-launch download + instead of being packed in (they are versioned separately and come in two + CPU tiers). + - **The `.AppImage.zsync` must be uploaded beside every AppImage.** The + embedded update information names it + (`gh-releases-zsync|…|latest|VapourBox-*-linux-.AppImage.zsync`), so + one missing is an updater that fails for every user. The script fails if + `zsyncmake` is absent rather than build without it, and + `packaging_test.dart` asserts the release upload picks both files up. + `latest` is GitHub's Latest release — one more reason a deps or whisper + release must never be marked Latest. + - The desktop file and icon are named for `APPLICATION_ID` + (`app.vapourbox.VapourBox`, `app/linux/CMakeLists.txt`), which is what + the window reports as its app id; rename one and the running window stops + matching its launcher. + - `appimagetool` is pinned by version **and** SHA-256 in the script. The + AppImage runtime it embeds is fetched by appimagetool itself at build time + and is not pinned. + - Not verifiable from macOS (appimagetool is a Linux binary): + `build-linux.yml` is the test. Plugin lists for all platforms: see `deps/` directories or download scripts. @@ -1583,7 +1612,7 @@ Notes: | `Scripts/check-deps-changed.sh` | Detect if deps changed since last release (exit 0=changed, 1=unchanged) | | `Scripts/package-macos.sh` | Package macOS app | | `Scripts/package-windows.ps1` | Package Windows app | -| `Scripts/package-linux.sh` | Package Linux app | +| `Scripts/package-linux.sh` | Package Linux app (AppImage + `.zsync` + tarball) | | `Scripts/package-deps-macos.sh` | Package macOS deps | | `Scripts/package-deps-windows.ps1` | Package Windows deps | | `Scripts/package-deps-linux.sh` | Package Linux deps | diff --git a/README.md b/README.md index 5de2655d..6f261828 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ VapourBox runs [VapourSynth](https://www.vapoursynth.com/), QTGMC and FFmpeg — | macOS (Apple Silicon) | macOS 15 Sequoia | `VapourBox-x.x.x-macos-arm64.dmg` | | macOS (Intel) | macOS 12 Monterey | `VapourBox-x.x.x-macos-x64.dmg` | | Windows (x64) | Windows 10/11 | `VapourBox-x.x.x-windows-x64.zip` | -| Linux (x64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-x64.tar.gz` | -| Linux (arm64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-arm64.tar.gz` | +| Linux (x64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-x64.AppImage` | +| Linux (arm64) | glibc 2.39 (Ubuntu 24.04, Debian 13) | `VapourBox-x.x.x-linux-arm64.AppImage` | All processing is local. There is no account, no telemetry, and nothing is uploaded. @@ -68,11 +68,15 @@ VapourBox is signed with an Apple Developer ID and notarized, so it opens withou
Installing on Linux -1. Download the `.tar.gz` for your architecture. -2. `tar -xzf VapourBox-x.x.x-linux-x64.tar.gz` -3. `cd VapourBox-x.x.x-linux-x64 && ./vapourbox` +1. Download the `.AppImage` for your architecture. +2. Make it executable: `chmod +x VapourBox-x.x.x-linux-x64.AppImage` (or tick *Allow executing file as program* in the file manager's Properties). +3. Run it: `./VapourBox-x.x.x-linux-x64.AppImage` 4. First launch downloads its processing dependencies (~185 MB). +The AppImage is a single file that needs no installation and can live anywhere. To get a menu entry and icon, open it with an AppImage manager such as [Gear Lever](https://github.com/mijorus/gearlever) or [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher); those, and [AppImageUpdate](https://github.com/AppImageCommunity/AppImageUpdate), can also update it in place, downloading only what changed. + +Each release also carries a plain `.tar.gz` of the same build, for systems where an AppImage can't be mounted: extract it and run `./vapourbox`. + GPU-accelerated deinterlacing (NNEDI3CL) needs your GPU's OpenCL driver installed. Without it, VapourBox falls back to the CPU automatically.
diff --git a/Scripts/ci-build-and-release.sh b/Scripts/ci-build-and-release.sh index 8da4768c..f92ccf82 100755 --- a/Scripts/ci-build-and-release.sh +++ b/Scripts/ci-build-and-release.sh @@ -185,7 +185,7 @@ echo " Downloading Linux artifact..." gh run download "$LINUX_RUN_ID" --dir "$DOWNLOAD_DIR" echo " Downloaded artifacts:" -find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.sha256.json" \) | while read -r f; do +find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do SIZE=$(du -sh "$f" | cut -f1) echo " $(basename "$f") ($SIZE)" done @@ -195,7 +195,7 @@ echo "" echo -e "${BLUE}[4/4] Uploading to release ${RELEASE_TAG}...${NC}" UPLOAD_COUNT=0 -find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.sha256.json" \) | while read -r f; do +find "$DOWNLOAD_DIR" -type f \( -name "*.zip" -o -name "*.dmg" -o -name "*.tar.gz" -o -name "*.AppImage" -o -name "*.AppImage.zsync" -o -name "*.sha256.json" \) | while read -r f; do BASENAME=$(basename "$f") echo " Uploading $BASENAME..." gh release upload "$RELEASE_TAG" "$f" --clobber diff --git a/Scripts/generate-app-icons.sh b/Scripts/generate-app-icons.sh index 80607315..2b2678d8 100755 --- a/Scripts/generate-app-icons.sh +++ b/Scripts/generate-app-icons.sh @@ -20,6 +20,7 @@ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" RENDER="$PROJECT_ROOT/Scripts/generate-app-icon.swift" ICONSET="$PROJECT_ROOT/app/macos/Runner/Assets.xcassets/AppIcon.appiconset" WIN_RES="$PROJECT_ROOT/app/windows/runner/resources" +LINUX_ICON="$PROJECT_ROOT/packaging/linux/app.vapourbox.VapourBox.png" if ! command -v swift >/dev/null 2>&1; then echo "ERROR: swift not found — install the Xcode command line tools." >&2 @@ -72,6 +73,11 @@ pathlib.Path(out).write_bytes(header + directory + b''.join(blobs)) print(f' {out} ({len(sizes)} sizes: {", ".join(map(str, sizes))})') PY +echo "==> Linux AppImage icon" +# One 256px PNG: the size the AppImage format asks for at the AppDir root, and +# what package-linux.sh installs under hicolor/256x256. +swift "$RENDER" --size 256 --style square --out "$LINUX_ICON" + echo "" echo "Done. Rebuild the app to see them:" echo " ./Scripts/run-debug-macos.sh --skip-worker" diff --git a/Scripts/package-linux.sh b/Scripts/package-linux.sh index 4ff7fb0c..215a3f75 100755 --- a/Scripts/package-linux.sh +++ b/Scripts/package-linux.sh @@ -1,27 +1,38 @@ #!/bin/bash # Package VapourBox for Linux -# Creates a distributable tarball with Flutter app + Rust worker + templates +# Creates an AppImage (the primary download) and a tarball (the fallback) from +# the same tree: Flutter app + Rust worker + templates. # # Prerequisites: # - Flutter SDK, Rust toolchain # - Worker and app already built (or use without --skip-build) +# - For the AppImage: zsyncmake (apt install zsync), and a host of the target +# architecture — appimagetool is fetched automatically, or set $APPIMAGETOOL # # Usage: ./Scripts/package-linux.sh --version X.Y.Z [--skip-build] [--arch x64|arm64] +# [--skip-appimage] set -e VERSION="1.0.0" SKIP_BUILD=false +SKIP_APPIMAGE=false ARCH="" +# Pinned, and checksummed below: this binary assembles what users run. +APPIMAGETOOL_VERSION="1.9.1" +GITHUB_REPO="StuartCameronCode/VapourBox" +APP_ID="app.vapourbox.VapourBox" + while [[ $# -gt 0 ]]; do case $1 in --version) VERSION="$2"; shift 2 ;; --skip-build) SKIP_BUILD=true; shift ;; --arch) ARCH="$2"; shift 2 ;; + --skip-appimage) SKIP_APPIMAGE=true; shift ;; *) echo "Unknown option: $1" - echo "Usage: $0 --version X.Y.Z [--skip-build] [--arch x64|arm64]" + echo "Usage: $0 --version X.Y.Z [--skip-build] [--arch x64|arm64] [--skip-appimage]" exit 1 ;; esac @@ -51,6 +62,9 @@ TOTAL_STEPS=4 if ! $SKIP_BUILD; then TOTAL_STEPS=5 fi +if ! $SKIP_APPIMAGE; then + TOTAL_STEPS=$((TOTAL_STEPS + 1)) +fi # Build if needed if ! $SKIP_BUILD; then @@ -118,16 +132,122 @@ cp -r "$PROJECT_ROOT/licenses/"* "$PACKAGE_DIR/licenses/" 2>/dev/null || true STEP=$((STEP + 1)) +# Create AppImage +# +# The AppImage holds exactly the tree the tarball does, under usr/lib/vapourbox, +# so the two cannot drift. Nothing inside it is written to at runtime: deps and +# add-ons install under $XDG_DATA_HOME, which is also why the processing +# dependencies stay a first-launch download rather than being packed in here. +APPIMAGE_FILE="" +if ! $SKIP_APPIMAGE; then + echo "[$STEP/$TOTAL_STEPS] Creating AppImage..." + + if [ "$(uname -s)" != "Linux" ]; then + echo "ERROR: an AppImage can only be built on Linux (or pass --skip-appimage)." + exit 1 + fi + + case "$ARCH" in + x64) + AI_ARCH="x86_64" + AI_SHA256="ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0" + ;; + arm64) + AI_ARCH="aarch64" + AI_SHA256="f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158" + ;; + *) + echo "ERROR: no AppImage architecture for --arch $ARCH" + exit 1 + ;; + esac + + if [ "$(uname -m)" != "$AI_ARCH" ]; then + echo "ERROR: building a $AI_ARCH AppImage needs a $AI_ARCH host (this is $(uname -m))." + echo " appimagetool is a native binary. Pass --skip-appimage for the tarball alone." + exit 1 + fi + + # Without zsyncmake appimagetool still succeeds, and still embeds update + # information — pointing at a .zsync that was never made. That is an + # updater that fails for every user, so it is an error here, not a warning. + if ! command -v zsyncmake >/dev/null 2>&1; then + echo "ERROR: zsyncmake not found (apt install zsync)." + echo " It generates the .zsync file the AppImage's update information points at." + exit 1 + fi + + if [ -z "$APPIMAGETOOL" ]; then + TOOLS_DIR="$DIST_DIR/.tools" + APPIMAGETOOL="$TOOLS_DIR/appimagetool-$APPIMAGETOOL_VERSION-$AI_ARCH.AppImage" + if [ ! -f "$APPIMAGETOOL" ]; then + mkdir -p "$TOOLS_DIR" + curl -fsSL -o "$APPIMAGETOOL.part" \ + "https://github.com/AppImage/appimagetool/releases/download/$APPIMAGETOOL_VERSION/appimagetool-$AI_ARCH.AppImage" + if ! echo "$AI_SHA256 $APPIMAGETOOL.part" | sha256sum -c - >/dev/null; then + echo "ERROR: appimagetool $APPIMAGETOOL_VERSION ($AI_ARCH) failed its checksum." + rm -f "$APPIMAGETOOL.part" + exit 1 + fi + mv "$APPIMAGETOOL.part" "$APPIMAGETOOL" + fi + chmod +x "$APPIMAGETOOL" + fi + + LINUX_PACKAGING="$PROJECT_ROOT/packaging/linux" + APPDIR="$DIST_DIR/$PACKAGE_NAME.AppDir" + rm -rf "$APPDIR" + mkdir -p "$APPDIR/usr/lib" \ + "$APPDIR/usr/share/applications" \ + "$APPDIR/usr/share/icons/hicolor/256x256/apps" + cp -r "$PACKAGE_DIR" "$APPDIR/usr/lib/vapourbox" + + install -m 755 "$LINUX_PACKAGING/AppRun" "$APPDIR/AppRun" + # appimagetool wants the desktop file and its icon at the root; the copies + # under usr/share are what desktop-integration tools install from. + cp "$LINUX_PACKAGING/$APP_ID.desktop" "$APPDIR/" + cp "$LINUX_PACKAGING/$APP_ID.desktop" "$APPDIR/usr/share/applications/" + cp "$LINUX_PACKAGING/$APP_ID.png" "$APPDIR/" + cp "$LINUX_PACKAGING/$APP_ID.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/" + ln -s "$APP_ID.png" "$APPDIR/.DirIcon" + + # "latest" follows GitHub's Latest release, which is why a deps or whisper + # release must never be marked Latest (see CLAUDE.md). The wildcard stands + # in for the version; the arch stays literal so x64 never updates to arm64. + APPIMAGE_FILE="$PACKAGE_NAME.AppImage" + UPDATE_INFO="gh-releases-zsync|${GITHUB_REPO%%/*}|${GITHUB_REPO##*/}|latest|VapourBox-*-linux-$ARCH.AppImage.zsync" + + # appimagetool writes the .zsync into the working directory. It is itself + # an AppImage; extract-and-run spares the build host from needing FUSE. + cd "$DIST_DIR" + rm -f "$APPIMAGE_FILE" "$APPIMAGE_FILE.zsync" + ARCH="$AI_ARCH" APPIMAGE_EXTRACT_AND_RUN=1 VERSION="$VERSION" \ + "$APPIMAGETOOL" --updateinformation "$UPDATE_INFO" "$APPDIR" "$APPIMAGE_FILE" + + if [ ! -s "$APPIMAGE_FILE" ] || [ ! -s "$APPIMAGE_FILE.zsync" ]; then + echo "ERROR: appimagetool did not produce both $APPIMAGE_FILE and $APPIMAGE_FILE.zsync" + exit 1 + fi + chmod +x "$APPIMAGE_FILE" + rm -rf "$APPDIR" + + STEP=$((STEP + 1)) +fi + # Create tarball echo "[$STEP/$TOTAL_STEPS] Creating tarball..." cd "$DIST_DIR" TAR_FILE="$PACKAGE_NAME.tar.gz" tar -czf "$TAR_FILE" "$PACKAGE_NAME" -# Calculate size and hash -TAR_SIZE=$(stat -c%s "$TAR_FILE" 2>/dev/null || stat -f%z "$TAR_FILE" 2>/dev/null) -TAR_SIZE_MB=$(echo "scale=1; $TAR_SIZE / 1048576" | bc) -SHA256=$(sha256sum "$TAR_FILE" 2>/dev/null | cut -d' ' -f1 || shasum -a 256 "$TAR_FILE" | cut -d' ' -f1) +file_size_mb() { + local bytes + bytes=$(stat -c%s "$1" 2>/dev/null || stat -f%z "$1" 2>/dev/null) + echo "scale=1; $bytes / 1048576" | bc +} +file_sha256() { + sha256sum "$1" 2>/dev/null | cut -d' ' -f1 || shasum -a 256 "$1" | cut -d' ' -f1 +} # Cleanup unpacked directory rm -rf "$PACKAGE_DIR" @@ -135,11 +255,22 @@ rm -rf "$PACKAGE_DIR" echo "" echo "=== Packaging Complete ===" echo "" -echo " Tarball: $DIST_DIR/$TAR_FILE" -echo " Size: ${TAR_SIZE_MB} MB" -echo " SHA256: $SHA256" +if [ -n "$APPIMAGE_FILE" ]; then + echo " AppImage: $DIST_DIR/$APPIMAGE_FILE" + echo " Size: $(file_size_mb "$APPIMAGE_FILE") MB" + echo " SHA256: $(file_sha256 "$APPIMAGE_FILE")" + echo " Update: $DIST_DIR/$APPIMAGE_FILE.zsync (upload it beside the AppImage)" + echo "" +fi +echo " Tarball: $DIST_DIR/$TAR_FILE" +echo " Size: $(file_size_mb "$TAR_FILE") MB" +echo " SHA256: $(file_sha256 "$TAR_FILE")" echo "" -echo "To install:" +echo "To run:" +if [ -n "$APPIMAGE_FILE" ]; then + echo " chmod +x $APPIMAGE_FILE && ./$APPIMAGE_FILE" + echo "or:" +fi echo " tar -xzf $TAR_FILE" echo " cd $PACKAGE_NAME" echo " ./vapourbox" diff --git a/Scripts/release.sh b/Scripts/release.sh index 12971adb..9ab32ff9 100755 --- a/Scripts/release.sh +++ b/Scripts/release.sh @@ -306,13 +306,13 @@ RELEASE_NOTES="## VapourBox $APP_VERSION - **Windows**: \`VapourBox-$APP_VERSION-windows-x64.zip\` - **macOS (Apple Silicon)**: \`VapourBox-$APP_VERSION-macos-arm64.zip\` (contains signed & notarized DMG) - **macOS (Intel)**: \`VapourBox-$APP_VERSION-macos-x64.zip\` (contains signed & notarized DMG) -- **Linux (x64)**: \`VapourBox-$APP_VERSION-linux-x64.tar.gz\` -- **Linux (arm64)**: \`VapourBox-$APP_VERSION-linux-arm64.tar.gz\` +- **Linux (x64)**: \`VapourBox-$APP_VERSION-linux-x64.AppImage\` (or the \`.tar.gz\`) +- **Linux (arm64)**: \`VapourBox-$APP_VERSION-linux-arm64.AppImage\` (or the \`.tar.gz\`) ### Installation - **Windows**: Extract zip and run \`vapourbox.exe\` - **macOS**: Open the DMG and drag VapourBox to Applications -- **Linux**: Extract tarball and run \`./vapourbox\` +- **Linux**: \`chmod +x\` the AppImage and run it — or extract the tarball and run \`./vapourbox\` ### Dependencies Dependencies are automatically downloaded on first launch (~185 MB). @@ -345,7 +345,7 @@ echo -e "${GREEN}╚════════════════════ echo "" echo "Next steps:" echo " 1. Wait for Windows and Linux builds to complete (if using GitHub Actions)" -echo " 2. Upload Windows zip and Linux tarballs to the draft release" +echo " 2. Upload the Windows zip and the Linux AppImages (each with its .zsync) and tarballs to the draft release" echo " 3. Test the release builds" echo " 4. Edit release notes" echo " 5. Publish the release" diff --git a/app/test/packaging_test.dart b/app/test/packaging_test.dart index 7cc1d614..61e66ffd 100644 --- a/app/test/packaging_test.dart +++ b/app/test/packaging_test.dart @@ -72,4 +72,61 @@ void main() { ); }); } + + // The Linux AppImage embeds update information naming a `.zsync` on the + // latest GitHub release. Three files have to agree on that name, and nothing + // fails at build time if they don't — the updater just finds nothing. + group('Linux AppImage update information', () { + final package = + File(p.join(root, 'Scripts', 'package-linux.sh')).readAsStringSync(); + + test('names the .zsync the script itself produces', () { + expect(package, contains(r'APPIMAGE_FILE="$PACKAGE_NAME.AppImage"')); + expect(package, contains(r'PACKAGE_NAME="VapourBox-$VERSION-linux-$ARCH"')); + expect( + package, + contains(r'|latest|VapourBox-*-linux-$ARCH.AppImage.zsync"'), + reason: 'the update pattern must be the output filename with the ' + 'version wildcarded, plus .zsync', + ); + }); + + test('the release upload carries the AppImage and its .zsync', () { + final upload = File(p.join(root, 'Scripts', 'ci-build-and-release.sh')) + .readAsStringSync(); + final finds = upload + .split('\n') + .where((l) => l.startsWith('find ') && l.contains('*.tar.gz')) + .toList(); + expect(finds, isNotEmpty); + for (final line in finds) { + expect(line, contains('"*.AppImage"')); + expect(line, contains('"*.AppImage.zsync"')); + } + + final workflow = + File(p.join(root, '.github', 'workflows', 'build-linux.yml')) + .readAsStringSync(); + for (final arch in ['x64', 'arm64']) { + expect(workflow, contains('-linux-$arch.AppImage\n')); + expect(workflow, contains('-linux-$arch.AppImage.zsync\n')); + } + }); + + test('the desktop entry and icon are named for the application id', () { + final cmake = File(p.join(root, 'app', 'linux', 'CMakeLists.txt')) + .readAsStringSync(); + final id = RegExp(r'set\(APPLICATION_ID "([^"]+)"\)') + .firstMatch(cmake)! + .group(1)!; + final dir = p.join(root, 'packaging', 'linux'); + final desktop = File(p.join(dir, '$id.desktop')); + expect(desktop.existsSync(), isTrue); + expect(File(p.join(dir, '$id.png')).existsSync(), isTrue); + final body = desktop.readAsStringSync(); + expect(body, contains('Icon=$id\n')); + expect(body, contains('StartupWMClass=$id\n')); + expect(package, contains('APP_ID="$id"')); + }); + }); } diff --git a/docs/BUILDING.md b/docs/BUILDING.md index cce5a0f6..0b54570b 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -246,7 +246,8 @@ dart run build_runner build --delete-conflicting-outputs `Scripts/generate-app-icon.swift` is the vector source of truth (CoreGraphics, no ImageMagick needed); `Scripts/generate-app-icons.sh` drives it at every size each -platform wants and packs the Windows `.ico`. macOS only, needs Swift from the CLT: +platform wants, packs the Windows `.ico` and writes the Linux AppImage icon +(`packaging/linux/app.vapourbox.VapourBox.png`). macOS only, needs Swift from the CLT: ```bash ./Scripts/generate-app-icons.sh @@ -269,7 +270,15 @@ Output: `dist/VapourBox-1.0.0-windows-x64.zip` ```bash ./Scripts/package-linux.sh --version 1.0.0 [--skip-build] [--arch x64|arm64] ``` -`--arch` defaults to the host. Output: `dist/VapourBox-1.0.0-linux-.tar.gz` +`--arch` defaults to the host. Output, all from the same tree: + +- `dist/VapourBox-1.0.0-linux-.AppImage` — the primary download +- `dist/VapourBox-1.0.0-linux-.AppImage.zsync` — its delta-update index; must be uploaded beside it +- `dist/VapourBox-1.0.0-linux-.tar.gz` — the fallback + +The AppImage step needs `zsyncmake` (`sudo apt install zsync`) and a host of the +target architecture; it downloads a pinned, checksummed `appimagetool` into +`dist/.tools/` (or uses `$APPIMAGETOOL`). `--skip-appimage` builds the tarball alone. ### macOS ```bash diff --git a/packaging/linux/AppRun b/packaging/linux/AppRun new file mode 100755 index 00000000..a4bda984 --- /dev/null +++ b/packaging/linux/AppRun @@ -0,0 +1,10 @@ +#!/bin/sh +# AppImage entry point. +# +# Deliberately sets no LD_LIBRARY_PATH (or anything else): the Flutter runner +# finds its own lib/ through an $ORIGIN rpath, and whatever this exports is +# inherited by the worker and by the ffmpeg/vspipe it spawns from the +# downloaded deps bundle, which must see only the environment the worker gives +# them. +HERE="$(dirname "$(readlink -f "$0")")" +exec "$HERE/usr/lib/vapourbox/vapourbox" "$@" diff --git a/packaging/linux/app.vapourbox.VapourBox.desktop b/packaging/linux/app.vapourbox.VapourBox.desktop new file mode 100644 index 00000000..b9c7bd16 --- /dev/null +++ b/packaging/linux/app.vapourbox.VapourBox.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Name=VapourBox +GenericName=Video Processor +Comment=Deinterlace, restore and convert video with VapourSynth and QTGMC +Exec=vapourbox +Icon=app.vapourbox.VapourBox +Terminal=false +Categories=AudioVideo;Video; +Keywords=deinterlace;QTGMC;VapourSynth;VHS;DV;DVD;convert; +StartupNotify=true +StartupWMClass=app.vapourbox.VapourBox diff --git a/packaging/linux/app.vapourbox.VapourBox.png b/packaging/linux/app.vapourbox.VapourBox.png new file mode 100644 index 0000000000000000000000000000000000000000..36882d444775ebf94847b88bbdf5690a68321e4b GIT binary patch literal 72389 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 pe}< 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 literal 0 HcmV?d00001 From 64e4f58db5ec73b3c2281c131c9b37a170aa2a07 Mon Sep 17 00:00:00 2001 From: Stuart Cameron Date: Mon, 5 Oct 2026 22:19:19 +1100 Subject: [PATCH 2/2] test: read packaging files with LF endings on a Windows checkout The AppImage lint tests asserted whole lines ending in \n. A Windows checkout gives the workflow and desktop entry CRLF, so two of them failed on the Windows CI job only. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014GLXdGLfPwgYjW1AkonGqN --- app/test/packaging_test.dart | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/test/packaging_test.dart b/app/test/packaging_test.dart index 61e66ffd..3fcf067a 100644 --- a/app/test/packaging_test.dart +++ b/app/test/packaging_test.dart @@ -29,6 +29,13 @@ String _repoRoot() { } } +/// Reads a repo file with LF endings. A Windows checkout gives these files +/// CRLF, so an assertion on a whole line (`'Icon=$id\n'`) fails there only. +String _read(String root, List parts) => + File(p.joinAll([root, ...parts])) + .readAsStringSync() + .replaceAll('\r\n', '\n'); + void main() { final root = _repoRoot(); @@ -77,12 +84,12 @@ void main() { // latest GitHub release. Three files have to agree on that name, and nothing // fails at build time if they don't — the updater just finds nothing. group('Linux AppImage update information', () { - final package = - File(p.join(root, 'Scripts', 'package-linux.sh')).readAsStringSync(); + final package = _read(root, ['Scripts', 'package-linux.sh']); test('names the .zsync the script itself produces', () { expect(package, contains(r'APPIMAGE_FILE="$PACKAGE_NAME.AppImage"')); - expect(package, contains(r'PACKAGE_NAME="VapourBox-$VERSION-linux-$ARCH"')); + expect( + package, contains(r'PACKAGE_NAME="VapourBox-$VERSION-linux-$ARCH"')); expect( package, contains(r'|latest|VapourBox-*-linux-$ARCH.AppImage.zsync"'), @@ -92,8 +99,7 @@ void main() { }); test('the release upload carries the AppImage and its .zsync', () { - final upload = File(p.join(root, 'Scripts', 'ci-build-and-release.sh')) - .readAsStringSync(); + final upload = _read(root, ['Scripts', 'ci-build-and-release.sh']); final finds = upload .split('\n') .where((l) => l.startsWith('find ') && l.contains('*.tar.gz')) @@ -104,9 +110,7 @@ void main() { expect(line, contains('"*.AppImage.zsync"')); } - final workflow = - File(p.join(root, '.github', 'workflows', 'build-linux.yml')) - .readAsStringSync(); + final workflow = _read(root, ['.github', 'workflows', 'build-linux.yml']); for (final arch in ['x64', 'arm64']) { expect(workflow, contains('-linux-$arch.AppImage\n')); expect(workflow, contains('-linux-$arch.AppImage.zsync\n')); @@ -114,16 +118,14 @@ void main() { }); test('the desktop entry and icon are named for the application id', () { - final cmake = File(p.join(root, 'app', 'linux', 'CMakeLists.txt')) - .readAsStringSync(); + final cmake = _read(root, ['app', 'linux', 'CMakeLists.txt']); final id = RegExp(r'set\(APPLICATION_ID "([^"]+)"\)') .firstMatch(cmake)! .group(1)!; final dir = p.join(root, 'packaging', 'linux'); - final desktop = File(p.join(dir, '$id.desktop')); - expect(desktop.existsSync(), isTrue); + expect(File(p.join(dir, '$id.desktop')).existsSync(), isTrue); expect(File(p.join(dir, '$id.png')).existsSync(), isTrue); - final body = desktop.readAsStringSync(); + final body = _read(root, ['packaging', 'linux', '$id.desktop']); expect(body, contains('Icon=$id\n')); expect(body, contains('StartupWMClass=$id\n')); expect(package, contains('APP_ID="$id"'));