Skip to content

Build fix/video/version/0.12.0 - 1 #162

Build fix/video/version/0.12.0 - 1

Build fix/video/version/0.12.0 - 1 #162

Workflow file for this run

name: Build AppFlowy Linux
run-name: Build ${{ github.event.inputs.branch }}/${{ github.event.inputs.build_name }} - ${{ github.event.inputs.internal_build }}
on:
workflow_dispatch:
inputs:
repo:
description: "Repo"
required: true
default: "AppFlowy-IO/AppFlowy-Premium"
branch:
description: "Branch"
required: true
default: "main"
build_name:
description: "Build Version (it should match the version in pubspec.yaml)"
required: true
default: "0.12.0"
internal_build:
type: choice
description: "Internal Build Type (1 for internal, 0 for external)"
required: true
default: "1"
options:
- "0"
- "1"
workflow_call:
inputs:
repo:
description: "Repo"
required: true
type: string
branch:
description: "Branch"
required: true
type: string
build_name:
description: "Build Version"
required: true
type: string
internal_build:
description: "Internal Build Type"
required: true
type: string
upload_url:
description: "Upload URL for release assets"
required: false
type: string
env:
FLUTTER_VERSION: "3.41.9"
RUST_TOOLCHAIN: "1.92.0"
jobs:
build:
strategy:
fail-fast: true
matrix:
os: [ubuntu-22.04]
include:
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
env:
LINUX_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ inputs.build_name }}/linux/Release
LINUX_ZIP_NAME: AppFlowy-${{ inputs.build_name }}-linux.tar.gz
LINUX_PACKAGE_DEB_NAME: AppFlowy-${{ inputs.build_name }}-linux-x86_64.deb
LINUX_PACKAGE_RPM_NAME: AppFlowy-${{ inputs.build_name }}-linux-x86_64.rpm
LINUX_PACKAGE_TMP_RPM_NAME: AppFlowy-${{ inputs.build_name }}-2.x86_64.rpm
LINUX_PACKAGE_APPIMAGE_NAME: AppFlowy-${{ inputs.build_name }}-linux-x86_64.AppImage
steps:
# the following step is required to avoid running out of space
- name: Maximize build space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- name: Checkout source code
uses: actions/checkout@v2
with:
repository: ${{ inputs.repo }}
ref: ${{ inputs.branch }}
token: ${{ secrets.PRIVATE_REPO_TOKEN }}
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.target }}
override: true
profile: minimal
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.os }}
workspaces: |
frontend/rust-lib
- uses: davidB/rust-cargo-make@v1
with:
version: "0.37.18"
- name: Install prerequisites
working-directory: frontend
run: |
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install -y --fix-broken
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev libcurl4-openssl-dev libepoxy-dev
sudo apt-get install keybinder-3.0 libnotify-dev libwayland-cursor0 libwayland-client0 libwayland-egl1 unzip
sudo apt-get -y install alien
sudo apt install libmpv-dev mpv
sudo apt install libasound2-dev
# Install GStreamer for audioplayers_linux
# Install libunwind-dev first to resolve dependency issues
sudo apt-get -y install libunwind-dev
sudo apt-get -y install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
# Install FUSE for AppImage support
sudo apt-get install -y fuse libfuse2
# dpkg-shlibdeps (dpkg-dev) computes the .deb runtime dependencies;
# the rest are tools appimage-builder shells out to while assembling
# the AppImage.
sudo apt-get install -y dpkg-dev patchelf desktop-file-utils libgtk-3-bin squashfs-tools zsync gnupg fakeroot strace
source $HOME/.cargo/env
cargo install duckscript_cli --force --locked
rustup target add ${{ matrix.target }}
shell: bash
- name: Generate env file
working-directory: frontend/appflowy_flutter
run: |
echo "INTERNAL_BUILD=${{ github.env.inputs.internal_build }}" >> .env
shell: bash
- name: Configure Git credentials for Cargo
run: |
git config --global credential.helper store
echo "https://${{ secrets.ADMIN_GITHUB_TOKEN }}:x-oauth-basic@github.com" > ~/.git-credentials
- name: Build AppFlowy
working-directory: frontend
run: |
flutter config --enable-linux-desktop
cd appflowy_flutter
flutter pub get
dart run pdfrx:remove_wasm_modules
trap 'cd "$GITHUB_WORKSPACE/frontend/appflowy_flutter" && dart run pdfrx:remove_wasm_modules --revert' EXIT
cd ..
dart ./scripts/flutter_release_build/build_flowy.dart run . ${{ inputs.build_name }}
- name: Archive Asset
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
run: tar -czf ${{ env.LINUX_ZIP_NAME }} *
- name: Bundle libmpv into AppFlowy bundle
run: |
set -euo pipefail
BUNDLE="appflowy_flutter/product/${{ inputs.build_name }}/linux/Release"
mkdir -p "$BUNDLE/libmpv"
# Download libmpv1 from Ubuntu jammy (22.04) for maximum compatibility
# This ensures the .so file is bundled and doesn't rely on system packages
wget -q -O /tmp/libmpv1.deb \
http://archive.ubuntu.com/ubuntu/pool/universe/m/mpv/libmpv1_0.34.1-1ubuntu3_amd64.deb
dpkg-deb -x /tmp/libmpv1.deb /tmp/libmpv-extracted
cp -r /tmp/libmpv-extracted/usr/lib/x86_64-linux-gnu/* "$BUNDLE/lib/"
# Also copy to libmpv subfolder for backup
cp /tmp/libmpv-extracted/usr/lib/x86_64-linux-gnu/libmpv.so.1* "$BUNDLE/libmpv/" 2>/dev/null || true
echo "✅ libmpv bundled into $BUNDLE/lib/"
- name: Compute .deb runtime dependencies
working-directory: frontend
run: |
set -euo pipefail
BUNDLE="appflowy_flutter/product/${{ inputs.build_name }}/linux/Release/AppFlowy"
CONTROL="scripts/linux_distribution/deb/DEBIAN/control"
if [ ! -x "$BUNDLE/AppFlowy" ]; then
echo "::error::AppFlowy binary not found at $BUNDLE/AppFlowy"
exit 1
fi
ABS_BUNDLE="$(pwd)/$BUNDLE"
# dpkg-shlibdeps maps every system shared library NEEDED by the
# executable and the bundled Flutter plugins (incl. media_kit ->
# libmpv.so.1) to its providing apt package. It expects a throwaway
# debian/ tree; --ignore-missing-info skips Flutter's own bundled
# .so files, which are not provided by any apt package.
WORK="$(mktemp -d)"
mkdir -p "$WORK/debian"
printf 'Source: appflowy\nMaintainer: AppFlowy\n\nPackage: appflowy\nArchitecture: amd64\nDescription: AppFlowy\n' > "$WORK/debian/control"
COMPUTED=""
if ( cd "$WORK" && dpkg-shlibdeps -O --ignore-missing-info \
"$ABS_BUNDLE/AppFlowy" "$ABS_BUNDLE"/lib/*.so ) > "$WORK/out" 2> "$WORK/err"; then
COMPUTED="$(sed -n 's/^shlibs:Depends=//p' "$WORK/out")"
fi
echo "---- dpkg-shlibdeps diagnostics ----"
cat "$WORK/err" || true
echo "computed Depends: ${COMPUTED:-<none>}"
# Curated fallback covering the critical runtime libraries, used only
# if shlibdeps fails outright. libmpv is NO LONGER listed here because
# we now bundle it inside the .deb - no external dependency needed.
ESSENTIAL="libgtk-3-0, libkeybinder-3.0-0, libnotify4, libgstreamer1.0-0, libgstreamer-plugins-base1.0-0, libasound2"
if [ -n "$COMPUTED" ]; then
DEPS="$COMPUTED"
else
echo "::warning::dpkg-shlibdeps produced no output; using curated dependency list"
DEPS="$ESSENTIAL"
fi
# Remove any libmpv reference from computed deps since we bundle it
DEPS="$(echo "$DEPS" | sed 's/libmpv1,//g' | sed 's/,libmpv1//g' | sed 's/libmpv2,//g' | sed 's/,libmpv2//g')"
python3 - "$CONTROL" "$DEPS" <<'PY'
import re, sys
path, deps = sys.argv[1], sys.argv[2]
text = open(path).read()
text = re.sub(r'(?m)^Depends:.*$', 'Depends: ' + deps, text, count=1)
open(path, 'w').write(text)
print("patched control ->", "Depends: " + deps)
PY
- name: Build Linux package (.deb)
working-directory: frontend
run: |
sh scripts/linux_distribution/deb/build_deb.sh appflowy_flutter/product/${{ inputs.build_name }}/linux/Release ${{ inputs.build_name }} ${{ env.LINUX_PACKAGE_DEB_NAME }}
- name: Build Linux package (.rpm)
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
run: |
sudo alien -r ${{ env.LINUX_PACKAGE_DEB_NAME }}
cp -r ${{ env.LINUX_PACKAGE_TMP_RPM_NAME }} ${{ env.LINUX_PACKAGE_RPM_NAME }}
- name: Build Linux package (.AppImage)
working-directory: frontend
continue-on-error: true
run: |
set -euo pipefail
echo "📦 Building AppImage with appimage-builder (auto-bundles libmpv & its deps)..."
# Enable FUSE user space file systems
echo 'user_allow_other' | sudo tee -a /etc/fuse.conf
# Download and bundle libmpv from Ubuntu jammy BEFORE appimage-builder runs.
# media_kit loads libmpv.so.1 at runtime via dlopen, which appimage-builder's
# ELF scanner may miss. We pre-bundle it to guarantee it's in the AppImage.
LIBMPV_DEB="/tmp/libmpv1.AppImage.bundle.deb"
mkdir -p /tmp/libmpv-AppImage
wget -q -O "$LIBMPV_DEB" \
http://archive.ubuntu.com/ubuntu/pool/universe/m/mpv/libmpv1_0.34.1-1ubuntu3_amd64.deb
dpkg-deb -x "$LIBMPV_DEB" /tmp/libmpv-AppImage/extracted
# Generate the appimage-builder recipe. appimage-builder scans every
# ELF in the bundle, resolves each NEEDED library (incl. the media_kit
# plugin's libmpv.so.1 and its ffmpeg/libass/... tree) to an apt
# package, bundles them, and generates a runtime that wires up the
# library path. This is the approach the public AppFlowy repo uses.
# libmpv is also listed explicitly so the result is correct even if
# auto-detection is skipped. ${{ inputs.build_name }} is the version.
cat > AppImageBuilder.yml <<'YML'
version: 1
script:
- rm -rf AppDir || true
- cp -r appflowy_flutter/build/linux/x64/release/bundle AppDir
- mkdir -p AppDir/usr/share/icons/hicolor/scalable/apps
- cp scripts/linux_distribution/packaging/appflowy.svg AppDir/usr/share/icons/hicolor/scalable/apps/
- mkdir -p AppDir/usr/lib/x86_64-linux-gnu
- cp -r /tmp/libmpv-AppImage/extracted/usr/lib/x86_64-linux-gnu/* AppDir/usr/lib/x86_64-linux-gnu/
AppDir:
path: ./AppDir
app_info:
id: io.appflowy.AppFlowy
name: AppFlowy
icon: appflowy.svg
version: "APPFLOWY_VERSION"
exec: AppFlowy
exec_args: $@
apt:
arch:
- amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy main restricted universe multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu jammy-security main restricted universe multiverse
include:
- libc6:amd64
- libnotify4:amd64
- libkeybinder-3.0-0:amd64
- libwayland-cursor0:amd64
- libwayland-client0:amd64
- libwayland-egl1:amd64
- libmpv1:amd64
files:
include: []
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
AppImage:
arch: x86_64
YML
sed -i "s/APPFLOWY_VERSION/${{ inputs.build_name }}/" AppImageBuilder.yml
# Download appimage-builder (same version proven in the public AppFlowy repo).
APPIMAGE_BUILDER="./appimage-builder-x86_64.AppImage"
if [ ! -x "$APPIMAGE_BUILDER" ]; then
wget -q -O "$APPIMAGE_BUILDER" \
https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
chmod +x "$APPIMAGE_BUILDER"
fi
# APPIMAGE_EXTRACT_AND_RUN lets the builder (itself an AppImage) run
# where FUSE is unavailable (CI runners).
if APPIMAGE_EXTRACT_AND_RUN=1 "$APPIMAGE_BUILDER" --recipe AppImageBuilder.yml --skip-tests; then
# appimage-builder emits AppFlowy-<version>-x86_64.AppImage in CWD
# (frontend), which is exactly where the upload step looks.
ls -la AppFlowy-*-x86_64.AppImage
else
echo "⚠️ AppImage build failed, but continuing..."
# Create a dummy AppImage file to prevent upload failures
touch "${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_APPIMAGE_NAME }}"
touch "AppFlowy-${{ inputs.build_name }}-x86_64.AppImage"
fi
- name: Upload ZIP
uses: actions/upload-artifact@v4
with:
name: ${{ env.LINUX_ZIP_NAME }}
path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_ZIP_NAME }}
- name: Upload ZIP to Release
if: inputs.upload_url != ''
run: |
filename="${{ env.LINUX_ZIP_NAME }}"
filepath="${{ env.LINUX_APP_RELEASE_PATH }}/$filename"
# URL encode the filename
encoded_filename=$(echo -n "$filename" | jq -sRr @uri)
# Remove the {?name,label} template from upload URL if present
upload_url=$(echo "${{ inputs.upload_url }}" | sed 's/{[^}]*}//g')
echo "Uploading $filename to release..."
curl -L \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$filepath" \
"${upload_url}?name=${encoded_filename}" \
--fail-with-body
- name: Upload Linux package
working-directory: frontend
run: |
ls -a
- name: Upload AppImage
uses: actions/upload-artifact@v4
with:
name: ${{ env.LINUX_PACKAGE_APPIMAGE_NAME}}
path: frontend/AppFlowy-${{ inputs.build_name }}-x86_64.AppImage
- name: Upload AppImage to Release
continue-on-error: true
if: inputs.upload_url != ''
run: |
filename="${{ env.LINUX_PACKAGE_APPIMAGE_NAME }}"
filepath="frontend/AppFlowy-${{ inputs.build_name }}-x86_64.AppImage"
# URL encode the filename
encoded_filename=$(echo -n "$filename" | jq -sRr @uri)
# Remove the {?name,label} template from upload URL if present
upload_url=$(echo "${{ inputs.upload_url }}" | sed 's/{[^}]*}//g')
echo "Uploading $filename to release..."
curl -L \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$filepath" \
"${upload_url}?name=${encoded_filename}" \
--fail-with-body
- name: Upload RPM
uses: actions/upload-artifact@v4
with:
name: ${{ env.LINUX_PACKAGE_RPM_NAME }}
path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_RPM_NAME }}
- name: Upload RPM to Release
if: inputs.upload_url != ''
run: |
filename="${{ env.LINUX_PACKAGE_RPM_NAME }}"
filepath="${{ env.LINUX_APP_RELEASE_PATH }}/$filename"
# URL encode the filename
encoded_filename=$(echo -n "$filename" | jq -sRr @uri)
# Remove the {?name,label} template from upload URL if present
upload_url=$(echo "${{ inputs.upload_url }}" | sed 's/{[^}]*}//g')
echo "Uploading $filename to release..."
curl -L \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$filepath" \
"${upload_url}?name=${encoded_filename}" \
--fail-with-body
- name: Upload DEB
uses: actions/upload-artifact@v4
with:
name: ${{ env.LINUX_PACKAGE_DEB_NAME }}
path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_DEB_NAME }}
- name: Upload DEB to Release
if: inputs.upload_url != ''
run: |
filename="${{ env.LINUX_PACKAGE_DEB_NAME }}"
filepath="${{ env.LINUX_APP_RELEASE_PATH }}/$filename"
# URL encode the filename
encoded_filename=$(echo -n "$filename" | jq -sRr @uri)
# Remove the {?name,label} template from upload URL if present
upload_url=$(echo "${{ inputs.upload_url }}" | sed 's/{[^}]*}//g')
echo "Uploading $filename to release..."
curl -L \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$filepath" \
"${upload_url}?name=${encoded_filename}" \
--fail-with-body