name: 'Flutter Build Linux' description: 'Installs GTK3/Linux build dependencies, builds Linux Flutter desktop apps, packages .tar.gz and .deb installers, and generates release metadata.' inputs: working_directory: description: 'Path to the Flutter app package directory (e.g., packages/sfw_desktop)' required: false default: '.' arch: description: 'Target Linux architecture (amd64 / x64 or arm64)' required: false default: 'amd64' build_args: description: 'Extra arguments passed to flutter build linux (e.g., --dart-define=...)' required: false default: '' install_deps: description: 'Install Linux GTK3 & CMake build dependencies via apt-get' required: false default: 'true' auto_create_linux_runner: description: 'Automatically run flutter create --platforms=linux . if linux/ directory is missing' required: false default: 'true' output_dir: description: 'Output directory for packaged .deb and .tar.gz archives' required: false default: 'dist' package_script: description: 'Optional custom packaging script in repository (e.g., scripts/package_linux.sh)' required: false default: 'scripts/package_linux.sh' app_name: description: 'Application display name for .desktop entry (used if package_script is not present)' required: false default: 'Slides for Worship' package_name: description: 'Debian package slug (e.g., slides-for-worship)' required: false default: 'slides-for-worship' binary_name: description: 'Compiled Linux executable name inside bundle (auto-detected if empty)' required: false default: '' icon_path: description: 'Path to app icon PNG relative to working_directory' required: false default: 'assets/icon.png' maintainer: description: 'Debian package maintainer string' required: false default: 'Rody Davis ' description: description: 'Debian package description' required: false default: 'Professional worship slide production and presentation studio.' generate_metadata_json: description: 'Whether to generate linux__metadata.json' required: false default: 'false' outputs: version: description: 'Resolved package version from pubspec.yaml' value: ${{ steps.build.outputs.version }} deb_path: description: 'Path to generated .deb package' value: ${{ steps.package.outputs.deb_path }} tar_path: description: 'Path to generated .tar.gz archive' value: ${{ steps.package.outputs.tar_path }} metadata_path: description: 'Path to generated linux__metadata.json' value: ${{ steps.package.outputs.metadata_path }} runs: using: 'composite' steps: - name: Install Linux Build Dependencies if: inputs.install_deps == 'true' && runner.os == 'Linux' shell: bash run: | if command -v apt-get >/dev/null 2>&1; then SUDO="" if [ "$(id -u)" -ne 0 ] && command -v sudo >/dev/null 2>&1; then SUDO="sudo" fi $SUDO apt-get update $SUDO apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev dpkg-dev fi - name: Build Linux Release id: build shell: bash env: WORK_DIR: ${{ inputs.working_directory }} ARCH: ${{ inputs.arch }} BUILD_ARGS: ${{ inputs.build_args }} AUTO_CREATE: ${{ inputs.auto_create_linux_runner }} run: | ROOT_DIR="$(pwd)" VERSION=$(grep '^version: ' "$WORK_DIR/pubspec.yaml" | head -n 1 | cut -d ' ' -f 2 | tr -d '\r') echo "version=$VERSION" >> "$GITHUB_OUTPUT" cd "$WORK_DIR" flutter config --enable-linux-desktop if [ ! -d "linux" ] && [ "$AUTO_CREATE" = "true" ]; then echo "Notice: '$WORK_DIR/linux' directory not found. Scaffolding Linux runner via 'flutter create --platforms=linux .'..." flutter create --platforms=linux . fi if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then # shellcheck disable=SC2086 flutter build linux --release --target-platform=linux-arm64 $BUILD_ARGS else # shellcheck disable=SC2086 flutter build linux --release $BUILD_ARGS fi cd "$ROOT_DIR" - name: Package Linux Installers (.deb & .tar.gz) & Metadata id: package shell: bash env: WORK_DIR: ${{ inputs.working_directory }} ARCH_INPUT: ${{ inputs.arch }} OUT_DIR: ${{ inputs.output_dir }} PKG_SCRIPT: ${{ inputs.package_script }} APP_NAME: ${{ inputs.app_name }} PKG_NAME: ${{ inputs.package_name }} BIN_NAME: ${{ inputs.binary_name }} ICON_REL: ${{ inputs.icon_path }} MAINTAINER: ${{ inputs.maintainer }} DESC: ${{ inputs.description }} VERSION: ${{ steps.build.outputs.version }} GEN_META: ${{ inputs.generate_metadata_json }} run: | ROOT_DIR="$(pwd)" if [ "$ARCH_INPUT" = "arm64" ] || [ "$ARCH_INPUT" = "aarch64" ]; then DEB_ARCH="arm64" BUNDLE_DIR="$WORK_DIR/build/linux/arm64/release/bundle" else DEB_ARCH="amd64" BUNDLE_DIR="$WORK_DIR/build/linux/x64/release/bundle" fi mkdir -p "$OUT_DIR" if [ -n "$PKG_SCRIPT" ] && [ -f "$PKG_SCRIPT" ]; then echo "Running repository packaging script: $PKG_SCRIPT..." bash "$PKG_SCRIPT" "$VERSION" "$DEB_ARCH" "$BUNDLE_DIR" else echo "Packaging .tar.gz and .deb directly..." TAR_NAME="${PKG_NAME}-linux-${DEB_ARCH}.tar.gz" tar -czf "$OUT_DIR/$TAR_NAME" -C "$BUNDLE_DIR" . DEB_DIR="$WORK_DIR/build/deb_pkg_${DEB_ARCH}" rm -rf "$DEB_DIR" mkdir -p "$DEB_DIR/DEBIAN" mkdir -p "$DEB_DIR/usr/bin" mkdir -p "$DEB_DIR/usr/share/${PKG_NAME}" mkdir -p "$DEB_DIR/usr/share/applications" mkdir -p "$DEB_DIR/usr/share/icons/hicolor/512x512/apps" cp -r "$BUNDLE_DIR"/* "$DEB_DIR/usr/share/${PKG_NAME}/" if [ -z "$BIN_NAME" ]; then BIN_NAME=$(grep '^name: ' "$WORK_DIR/pubspec.yaml" | head -n 1 | cut -d ' ' -f 2 | tr -d '\r') fi ln -sf "/usr/share/${PKG_NAME}/${BIN_NAME}" "$DEB_DIR/usr/bin/${PKG_NAME}" if [ -f "$WORK_DIR/$ICON_REL" ]; then cp "$WORK_DIR/$ICON_REL" "$DEB_DIR/usr/share/icons/hicolor/512x512/apps/${PKG_NAME}.png" fi cat < "$DEB_DIR/usr/share/applications/${PKG_NAME}.desktop" [Desktop Entry] Name=$APP_NAME Comment=$DESC Exec=/usr/bin/$PKG_NAME Icon=$PKG_NAME Terminal=false Type=Application Categories=AudioVideo;Presentation;Utility; EOF CLEAN_VERSION=$(echo "$VERSION" | cut -d'+' -f1) cat < "$DEB_DIR/DEBIAN/control" Package: $PKG_NAME Version: $CLEAN_VERSION Section: utils Priority: optional Architecture: $DEB_ARCH Maintainer: $MAINTAINER Description: $APP_NAME $DESC EOF dpkg-deb --build "$DEB_DIR" "$OUT_DIR/${PKG_NAME}-linux-${DEB_ARCH}.deb" fi DEB_FILE=$(find "$OUT_DIR" -name "*-linux-${DEB_ARCH}.deb" 2>/dev/null | head -n 1 || true) TAR_FILE=$(find "$OUT_DIR" -name "*-linux-${DEB_ARCH}.tar.gz" 2>/dev/null | head -n 1 || true) echo "deb_path=$DEB_FILE" >> "$GITHUB_OUTPUT" echo "tar_path=$TAR_FILE" >> "$GITHUB_OUTPUT" if [ "$GEN_META" = "true" ]; then PRIMARY_FILE="${DEB_FILE:-$TAR_FILE}" SIZE="0" FILENAME="${PKG_NAME}-linux-${DEB_ARCH}.deb" if [ -n "$PRIMARY_FILE" ] && [ -f "$PRIMARY_FILE" ]; then SIZE=$(stat -c%s "$PRIMARY_FILE" 2>/dev/null || stat -f%z "$PRIMARY_FILE" 2>/dev/null || echo "0") FILENAME=$(basename "$PRIMARY_FILE") fi META_FILE="linux_${DEB_ARCH}_metadata.json" cat < "$META_FILE" { "version": "$VERSION", "platform": "linux", "arch": "$DEB_ARCH", "filename": "$FILENAME", "size": $SIZE, "signature": "", "signatureType": "none" } EOF cat "$META_FILE" echo "metadata_path=$META_FILE" >> "$GITHUB_OUTPUT" fi