Build and Release #17
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: Build Executables | |
| on: | |
| push: | |
| tags: | |
| - 'v*' # Trigger on version tags | |
| workflow_dispatch: # Allow manual trigger | |
| jobs: | |
| build: | |
| name: Build on ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [windows-latest, macos-latest, ubuntu-latest] | |
| include: | |
| - os: windows-latest | |
| output_name: clipmorph.exe | |
| asset_name: clipmorph-windows.exe | |
| ffmpeg_dir: windows | |
| - os: macos-latest | |
| output_name: clipmorph | |
| asset_name: clipmorph-macos | |
| ffmpeg_dir: mac | |
| - os: ubuntu-latest | |
| output_name: clipmorph | |
| asset_name: clipmorph-linux | |
| ffmpeg_dir: linux | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| lfs: true | |
| - name: Checkout LFS objects | |
| run: git lfs pull | |
| - name: Free up disk space (Linux only) | |
| if: runner.os == 'Linux' | |
| run: | | |
| echo "=== Before cleanup ===" | |
| df -h | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /usr/local/share/boost | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| sudo apt-get clean | |
| docker system prune -af | |
| echo "=== After cleanup ===" | |
| df -h | |
| shell: bash | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.11' | |
| - name: Set execute permissions for FFmpeg binaries (Unix only) | |
| if: runner.os != 'Windows' | |
| run: | | |
| if [ "$RUNNER_OS" == "macOS" ]; then | |
| chmod +x clipmorph/ffmpeg/mac/ffmpeg | |
| chmod +x clipmorph/ffmpeg/mac/ffprobe | |
| echo "Set permissions for macOS binaries" | |
| elif [ "$RUNNER_OS" == "Linux" ]; then | |
| chmod +x clipmorph/ffmpeg/linux/ffmpeg | |
| chmod +x clipmorph/ffmpeg/linux/ffprobe | |
| echo "Set permissions for Linux binaries" | |
| fi | |
| shell: bash | |
| - name: Remove unnecessary FFmpeg binaries to save space | |
| run: | | |
| if [ "$RUNNER_OS" == "macOS" ]; then | |
| rm -rf clipmorph/ffmpeg/windows clipmorph/ffmpeg/linux | |
| echo "Removed Windows and Linux FFmpeg binaries" | |
| elif [ "$RUNNER_OS" == "Linux" ]; then | |
| rm -rf clipmorph/ffmpeg/windows clipmorph/ffmpeg/mac | |
| echo "Removed Windows and macOS FFmpeg binaries" | |
| elif [ "$RUNNER_OS" == "Windows" ]; then | |
| rm -rf clipmorph/ffmpeg/mac clipmorph/ffmpeg/linux | |
| echo "Removed macOS and Linux FFmpeg binaries" | |
| fi | |
| shell: bash | |
| - name: Verify FFmpeg binaries | |
| run: | | |
| if [ "$RUNNER_OS" == "macOS" ]; then | |
| ls -lh clipmorph/ffmpeg/mac/ | |
| ./clipmorph/ffmpeg/mac/ffmpeg -version | |
| elif [ "$RUNNER_OS" == "Linux" ]; then | |
| ls -lh clipmorph/ffmpeg/linux/ | |
| ./clipmorph/ffmpeg/linux/ffmpeg -version | |
| elif [ "$RUNNER_OS" == "Windows" ]; then | |
| ls clipmorph/ffmpeg/windows/ | |
| ./clipmorph/ffmpeg/windows/ffmpeg.exe -version | |
| fi | |
| shell: bash | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip --no-cache-dir | |
| pip install pyinstaller --no-cache-dir | |
| pip install . --no-cache-dir | |
| - name: Create PyInstaller Spec | |
| run: | | |
| if [ "$RUNNER_OS" == "Windows" ] || [ "$RUNNER_OS" == "macOS" ]; then | |
| # Single executable for Windows and macOS | |
| cat > clipmorph.spec << 'EOF' | |
| # -*- mode: python ; coding: utf-8 -*- | |
| import sys | |
| excludes = [ | |
| 'pytest', 'unittest', 'nose', 'hypothesis', | |
| 'sphinx', 'docutils', 'IPython', 'jupyter', | |
| 'tkinter', 'PyQt5', 'PyQt6', 'PySide2', 'PySide6', | |
| 'tensorflow', 'tensorboard', 'jax', 'flax', | |
| 'scipy.spatial', 'scipy.ndimage', 'scipy.signal.windows', | |
| 'matplotlib.backends.backend_qt5agg', | |
| 'matplotlib.backends.backend_tkagg', | |
| 'matplotlib.backends.backend_wxagg', | |
| 'sympy', 'bokeh', 'dask', 'distributed', | |
| 'setuptools', 'pip', 'wheel', 'distutils', | |
| ] | |
| a = Analysis( | |
| ['clipmorph/__main__.py'], | |
| pathex=[], | |
| binaries=[], | |
| datas=[ | |
| ('clipmorph/ffmpeg/${{ matrix.ffmpeg_dir }}', 'clipmorph/ffmpeg/${{ matrix.ffmpeg_dir }}'), | |
| ('clipmorph/resources', 'clipmorph/resources'), | |
| ], | |
| hiddenimports=['whisper', 'whisperx', 'torch', 'torchaudio'], | |
| hookspath=[], | |
| hooksconfig={'matplotlib': {'backends': ['Agg']}}, | |
| runtime_hooks=[], | |
| excludes=excludes, | |
| noarchive=False, | |
| ) | |
| seen = set() | |
| filtered_binaries = [] | |
| for binary in a.binaries: | |
| if binary[0] not in seen: | |
| seen.add(binary[0]) | |
| filtered_binaries.append(binary) | |
| a.binaries = filtered_binaries | |
| pyz = PYZ(a.pure) | |
| exe = EXE( | |
| pyz, | |
| a.scripts, | |
| a.binaries, | |
| a.datas, | |
| [], | |
| name='${{ matrix.output_name }}', | |
| debug=False, | |
| bootloader_ignore_signals=False, | |
| strip=False, | |
| upx=False, | |
| console=True, | |
| disable_windowed_traceback=False, | |
| argv_emulation=False, | |
| target_arch=None, | |
| codesign_identity=None, | |
| entitlements_file=None, | |
| ) | |
| EOF | |
| else | |
| # Directory-based build for Linux | |
| cat > clipmorph.spec << 'EOF' | |
| # -*- mode: python ; coding: utf-8 -*- | |
| import sys | |
| excludes = [ | |
| 'pytest', 'unittest', 'nose', 'hypothesis', | |
| 'sphinx', 'docutils', 'IPython', 'jupyter', | |
| 'tkinter', 'PyQt5', 'PyQt6', 'PySide2', 'PySide6', | |
| 'tensorflow', 'tensorboard', 'jax', 'flax', | |
| 'scipy.spatial', 'scipy.ndimage', 'scipy.signal.windows', | |
| 'matplotlib.backends.backend_qt5agg', | |
| 'matplotlib.backends.backend_tkagg', | |
| 'matplotlib.backends.backend_wxagg', | |
| 'sympy', 'bokeh', 'dask', 'distributed', | |
| 'setuptools', 'pip', 'wheel', 'distutils', | |
| ] | |
| a = Analysis( | |
| ['clipmorph/__main__.py'], | |
| pathex=[], | |
| binaries=[], | |
| datas=[ | |
| ('clipmorph/ffmpeg/${{ matrix.ffmpeg_dir }}', 'clipmorph/ffmpeg/${{ matrix.ffmpeg_dir }}'), | |
| ('clipmorph/resources', 'clipmorph/resources'), | |
| ], | |
| hiddenimports=['whisper', 'whisperx', 'torch', 'torchaudio'], | |
| hookspath=[], | |
| hooksconfig={'matplotlib': {'backends': ['Agg']}}, | |
| runtime_hooks=[], | |
| excludes=excludes, | |
| noarchive=False, | |
| ) | |
| seen = set() | |
| filtered_binaries = [] | |
| for binary in a.binaries: | |
| if binary[0] not in seen: | |
| seen.add(binary[0]) | |
| filtered_binaries.append(binary) | |
| a.binaries = filtered_binaries | |
| pyz = PYZ(a.pure) | |
| exe = EXE( | |
| pyz, | |
| a.scripts, | |
| [], | |
| exclude_binaries=True, | |
| name='${{ matrix.output_name }}', | |
| debug=False, | |
| bootloader_ignore_signals=False, | |
| strip=False, | |
| upx=False, | |
| console=True, | |
| disable_windowed_traceback=False, | |
| argv_emulation=False, | |
| target_arch=None, | |
| codesign_identity=None, | |
| entitlements_file=None, | |
| ) | |
| coll = COLLECT( | |
| exe, | |
| a.binaries, | |
| a.datas, | |
| strip=False, | |
| upx=False, | |
| upx_exclude=[], | |
| name='clipmorph', | |
| ) | |
| EOF | |
| fi | |
| shell: bash | |
| - name: Build with PyInstaller | |
| run: pyinstaller clipmorph.spec | |
| - name: Analyze build size | |
| run: | | |
| echo "=== Build size analysis ===" | |
| if [ "$RUNNER_OS" == "Windows" ] || [ "$RUNNER_OS" == "macOS" ]; then | |
| du -sh dist/${{ matrix.output_name }} || true | |
| else | |
| du -sh dist/clipmorph || true | |
| fi | |
| echo "" | |
| echo "=== Disk space after build ===" | |
| df -h | |
| shell: bash | |
| continue-on-error: true | |
| - name: Package distribution | |
| run: | | |
| if [ "$RUNNER_OS" == "Windows" ] || [ "$RUNNER_OS" == "macOS" ]; then | |
| # Single executable - just move it | |
| mv dist/${{ matrix.output_name }} ${{ matrix.asset_name }} | |
| else | |
| # Linux: Create self-extracting archive | |
| cd dist | |
| tar -czf payload.tar.gz clipmorph/ | |
| # Create self-extracting shell script | |
| cat > ../clipmorph-linux << 'SELFEXTRACT' | |
| #!/bin/bash | |
| # Self-extracting installer for ClipMorph | |
| EXTRACT_DIR="$HOME/.clipmorph" | |
| PAYLOAD_LINE=$(awk '/^__PAYLOAD_BEGINS__/ { print NR + 1; exit 0; }' "$0") | |
| # Extract on first run or if extraction directory doesn't exist | |
| if [ ! -d "$EXTRACT_DIR" ] || [ ! -f "$EXTRACT_DIR/clipmorph/clipmorph" ]; then | |
| echo "Extracting ClipMorph (first run only)..." | |
| mkdir -p "$EXTRACT_DIR" | |
| tail -n +$PAYLOAD_LINE "$0" | tar -xzf - -C "$EXTRACT_DIR" | |
| echo "Extraction complete!" | |
| fi | |
| # Run the actual executable with all passed arguments | |
| exec "$EXTRACT_DIR/clipmorph/clipmorph" "$@" | |
| __PAYLOAD_BEGINS__ | |
| SELFEXTRACT | |
| # Append the compressed payload | |
| cat payload.tar.gz >> ../clipmorph-linux | |
| chmod +x ../clipmorph-linux | |
| fi | |
| shell: bash | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.asset_name }} | |
| path: ${{ matrix.asset_name }} | |
| - name: Create Release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| files: ${{ matrix.asset_name }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |