diff --git a/.gitattributes b/.gitattributes index 526c8a3..f6c180a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -*.sh text eol=lf \ No newline at end of file +*.sh text eol=lf +*.yml text eol=lf +*.yaml text eol=lf diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 3c4b385..ce344a3 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -1,119 +1,120 @@ -name: Build Desktop Apps - -on: - workflow_dispatch: - inputs: - build_windows: - description: Build the Windows package - type: boolean - default: true - build_macos: - description: Build the macOS package - type: boolean - default: true - refresh_flet: - description: Re-download the Flet desktop archive - type: boolean - default: false - -permissions: - contents: read - -concurrency: - group: build-desktop-${{ github.ref }} - cancel-in-progress: false - -jobs: - build-windows: - name: Build Windows - if: ${{ inputs.build_windows }} - runs-on: windows-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - cache: pip - cache-dependency-path: requirements.txt - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pyinstaller - - - name: Prepare bundled FFmpeg - run: python scripts/download_ffmpeg.py --platform windows - - - name: Prepare bundled Node.js - run: python scripts/download_nodejs.py --platform windows --windows-arch x64 - - - name: Build app - run: | - $buildArgs = @("--platform", "windows") - if ("${{ inputs.refresh_flet }}" -eq "true") { - $buildArgs += "--refresh-flet" - } - python scripts/build.py @buildArgs - - - name: Package zip - run: Compress-Archive -Path dist/StreamCap/* -DestinationPath dist/StreamCap-windows.zip -Force - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: StreamCap-windows - path: dist/StreamCap-windows.zip - if-no-files-found: error - - build-macos: - name: Build macOS - if: ${{ inputs.build_macos }} - runs-on: macos-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.10" - cache: pip - cache-dependency-path: requirements.txt - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pyinstaller - - - name: Prepare bundled FFmpeg - run: python scripts/download_ffmpeg.py --platform macos - - - name: Prepare bundled Node.js - run: python scripts/download_nodejs.py --platform macos - - - name: Build app - run: | - REFRESH_ARG="" - if [ "${{ inputs.refresh_flet }}" = "true" ]; then - REFRESH_ARG="--refresh-flet" - fi - python scripts/build.py --platform macos $REFRESH_ARG - - - name: Package dmg - run: | - perl -pi -e 's/\r$//' scripts/create_macos_dmg.sh - bash scripts/create_macos_dmg.sh "dist/StreamCap.app" "dist/StreamCap-macos.dmg" - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: StreamCap-macos - path: dist/StreamCap-macos.dmg - if-no-files-found: error +name: Build Desktop Apps + +on: + workflow_dispatch: + inputs: + build_windows: + description: Build the Windows package + type: boolean + default: true + build_macos: + description: Build the macOS package + type: boolean + default: true + refresh_flet: + description: Re-download the Flet desktop archive + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: build-desktop-${{ github.ref }} + cancel-in-progress: false + +jobs: + build-windows: + name: Build Windows + if: ${{ inputs.build_windows }} + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: pip + cache-dependency-path: requirements.txt + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Prepare bundled FFmpeg + run: python scripts/download_ffmpeg.py --platform windows + + - name: Prepare bundled Node.js + run: python scripts/download_nodejs.py --platform windows --windows-arch x64 + + - name: Build app + run: | + $buildArgs = @("--platform", "windows") + if ("${{ inputs.refresh_flet }}" -eq "true") { + $buildArgs += "--refresh-flet" + } + python scripts/build.py @buildArgs + + - name: Package zip + run: Compress-Archive -Path dist/StreamCap/* -DestinationPath dist/StreamCap-windows.zip -Force + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: StreamCap-windows + path: dist/StreamCap-windows.zip + if-no-files-found: error + + build-macos: + name: Build macOS + if: ${{ inputs.build_macos }} + runs-on: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: pip + cache-dependency-path: requirements.txt + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pyinstaller + + - name: Prepare bundled FFmpeg + run: python scripts/download_ffmpeg.py --platform macos + + - name: Prepare bundled Node.js + run: python scripts/download_nodejs.py --platform macos + + - name: Build app + run: | + REFRESH_ARG="" + if [ "${{ inputs.refresh_flet }}" = "true" ]; then + REFRESH_ARG="--refresh-flet" + fi + python scripts/build.py --platform macos $REFRESH_ARG + + - name: Package dmg + run: | + python -c "from pathlib import Path; path = Path('scripts/create_macos_dmg.sh'); path.write_bytes(path.read_bytes().replace(b'\r\n', b'\n'))" + bash -n scripts/create_macos_dmg.sh + bash scripts/create_macos_dmg.sh "dist/StreamCap.app" "dist/StreamCap-macos.dmg" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: StreamCap-macos + path: dist/StreamCap-macos.dmg + if-no-files-found: error diff --git a/scripts/create_macos_dmg.sh b/scripts/create_macos_dmg.sh index 79683c3..90dd3b4 100644 --- a/scripts/create_macos_dmg.sh +++ b/scripts/create_macos_dmg.sh @@ -1,83 +1,100 @@ -#!/usr/bin/env bash -set -euo pipefail - -APP_PATH="${1:-dist/StreamCap.app}" -DMG_PATH="${2:-dist/StreamCap-macos.dmg}" -VOLUME_NAME="${3:-StreamCap}" -BACKGROUND_IMAGE="${4:-assets/images/dmg.jpg}" - -if [ ! -d "$APP_PATH" ]; then - echo "App bundle not found: $APP_PATH" >&2 - exit 1 -fi - -if [ ! -f "$BACKGROUND_IMAGE" ]; then - echo "DMG background image not found: $BACKGROUND_IMAGE" >&2 - exit 1 -fi - -WORK_DIR="$(mktemp -d)" -MOUNT_DIR="$WORK_DIR/mount" -RW_DMG="$WORK_DIR/$VOLUME_NAME-rw.dmg" -DEVICE="" - -cleanup() { - if [ -n "$DEVICE" ]; then - hdiutil detach "$DEVICE" -quiet || true - fi - rm -rf "$WORK_DIR" -} -trap cleanup EXIT - -mkdir -p "$MOUNT_DIR" - -APP_SIZE_MB="$(du -sm "$APP_PATH" | awk '{print $1}')" -DMG_SIZE_MB="$((APP_SIZE_MB + 200))" - -hdiutil create "$RW_DMG" \ - -volname "$VOLUME_NAME" \ - -size "${DMG_SIZE_MB}m" \ - -fs HFS+ \ - -format UDRW \ - -quiet - -DEVICE="$(hdiutil attach "$RW_DMG" -readwrite -noverify -noautoopen -mountpoint "$MOUNT_DIR" | awk '/Apple_HFS/ {print $1; exit}')" - -cp -R "$APP_PATH" "$MOUNT_DIR/" -ln -s /Applications "$MOUNT_DIR/Applications" -mkdir -p "$MOUNT_DIR/.background" -cp "$BACKGROUND_IMAGE" "$MOUNT_DIR/.background/background.jpg" - -osascript <&2 + exit 1 +fi + +if [ ! -f "$BACKGROUND_IMAGE" ]; then + echo "DMG background image not found: $BACKGROUND_IMAGE" >&2 + exit 1 +fi + +WORK_DIR="$(mktemp -d)" +MOUNT_DIR="$WORK_DIR/mount" +RW_DMG="$WORK_DIR/$VOLUME_NAME-rw.dmg" +DEVICE="" + +log() { + echo "==> $*" +} + +cleanup() { + if [ -n "$DEVICE" ]; then + hdiutil detach "$DEVICE" -quiet || true + fi + rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +mkdir -p "$MOUNT_DIR" + +APP_SIZE_MB="$(du -sm "$APP_PATH" | awk '{print $1}')" +DMG_SIZE_MB="$((APP_SIZE_MB + 200))" + +log "Creating writable DMG image (${DMG_SIZE_MB} MB)" +hdiutil create "$RW_DMG" \ + -volname "$VOLUME_NAME" \ + -size "${DMG_SIZE_MB}m" \ + -fs HFS+ \ + -format UDRW + +log "Mounting writable DMG" +ATTACH_OUTPUT="$(hdiutil attach "$RW_DMG" -readwrite -noverify -noautoopen -mountpoint "$MOUNT_DIR")" +echo "$ATTACH_OUTPUT" +DEVICE="$(echo "$ATTACH_OUTPUT" | awk '/Apple_HFS/ {print $1; exit}')" +if [ -z "$DEVICE" ]; then + echo "Could not determine mounted DMG device." >&2 + exit 1 +fi + +log "Copying app bundle and installer assets" +cp -R "$APP_PATH" "$MOUNT_DIR/" +ln -s /Applications "$MOUNT_DIR/Applications" +mkdir -p "$MOUNT_DIR/.background" +cp "$BACKGROUND_IMAGE" "$MOUNT_DIR/.background/background.jpg" + +log "Configuring Finder window layout" +if ! osascript <&2 +fi + +log "Detaching writable DMG" +hdiutil detach "$DEVICE" -quiet +DEVICE="" + +log "Converting writable DMG to compressed image" +mkdir -p "$(dirname "$DMG_PATH")" +hdiutil convert "$RW_DMG" \ + -format UDZO \ + -imagekey zlib-level=9 \ + -o "$DMG_PATH" \ + -ov + +echo "Created DMG: $DMG_PATH"