Build & Release #42
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 & Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| strategy: | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| artifact: PDFApps-Windows | |
| - os: ubuntu-22.04 | |
| artifact: PDFApps-Linux | |
| - os: macos-latest | |
| artifact: PDFApps-macOS | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.14' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| # ── Windows ───────────────────────────────────────────────── | |
| - name: Build (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| python -m PyInstaller --noconfirm pdfapps.spec | |
| python -m PyInstaller --noconfirm uninstaller.spec | |
| python -m PyInstaller --noconfirm installer.spec | |
| - name: Upload (Windows) | |
| if: runner.os == 'Windows' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: | | |
| dist/PDFApps.exe | |
| dist/PDFAppsSetup.exe | |
| dist/PDFAppsUninstall.exe | |
| # ── Linux ─────────────────────────────────────────────────── | |
| - name: Install Linux system deps | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libegl1 libxkbcommon0 libxcb-cursor0 | |
| - name: Build (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| python -m PyInstaller --noconfirm pdfapps.spec | |
| chmod +x dist/PDFApps | |
| tar -czf dist/PDFApps-Linux.tar.gz -C dist PDFApps | |
| - name: Upload (Linux) | |
| if: runner.os == 'Linux' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: dist/PDFApps-Linux.tar.gz | |
| # ── macOS ─────────────────────────────────────────────────── | |
| - name: Free macOS disk space | |
| if: runner.os == 'macOS' | |
| # macos-latest ships with Xcode (~16 GB) which our pure-Python | |
| # build doesn't use. Removing it plus dropping APFS local | |
| # snapshots frees enough disk for hdiutil to create the DMG. | |
| # We keep CommandLineTools because PyInstaller calls | |
| # lipo / codesign / otool to slice and re-sign Mach-O binaries | |
| # — without those, the build dies with "lipo command ... | |
| # failed with error code 1". | |
| run: | | |
| echo "── before cleanup ──" | |
| df -h / | |
| sudo rm -rf /Applications/Xcode*.app | |
| # Default xcode-select path points at the (now gone) Xcode | |
| # bundle; redirect to CommandLineTools so /usr/bin/lipo and | |
| # friends keep resolving. | |
| sudo xcode-select -s /Library/Developer/CommandLineTools || true | |
| # Without this, the bytes freed by `rm -rf Xcode` are still | |
| # reserved by Time Machine local snapshots and df doesn't | |
| # actually drop. | |
| sudo tmutil deletelocalsnapshots / || true | |
| echo "── after cleanup ──" | |
| df -h / | |
| - name: Build (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| echo "── before PyInstaller ──" | |
| df -h | |
| python -m PyInstaller --noconfirm pdfapps.spec | |
| echo "── after PyInstaller ──" | |
| df -h | |
| du -sh dist/PDFApps.app | |
| # PyInstaller intermediates (~3-7 GB in build/ + the | |
| # bootloader cache) and the loose Unix exe under dist/ are | |
| # not needed for the DMG — only dist/PDFApps.app is. | |
| rm -rf build/ | |
| rm -rf "$HOME/Library/Application Support/pyinstaller" | |
| rm -f dist/PDFApps | |
| # Create DMG with the .app bundle and an Applications | |
| # shortcut. `mv` (not cp -R) — we don't need to keep the | |
| # source .app after the DMG is built. | |
| mkdir -p dist/dmg | |
| mv dist/PDFApps.app dist/dmg/ | |
| ln -s /Applications dist/dmg/Applications | |
| du -sh dist/dmg | |
| # The previous `hdiutil create -srcfolder` (no -size) | |
| # auto-estimated the intermediate sparse image too tight and | |
| # died with "No space left on device" while *copying into | |
| # the new DMG* — i.e. the runner had 83 GB free, but the | |
| # mounted image at /Volumes/PDFApps/ ran out of bytes mid- | |
| # copy. The .app has thousands of tiny files (Qt | |
| # frameworks, qtawesome fonts, etc.) so HFS+ slack space | |
| # blows past the auto-estimate. Force the intermediate to | |
| # 1 GB; UDZO compression makes the final .dmg much smaller. | |
| hdiutil create -volname "PDFApps" \ | |
| -srcfolder dist/dmg \ | |
| -ov -format UDZO \ | |
| -size 1g \ | |
| dist/PDFApps-macOS.dmg | |
| echo "── after DMG ──" | |
| df -h | |
| ls -lh dist/PDFApps-macOS.dmg | |
| - name: Upload (macOS) | |
| if: runner.os == 'macOS' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: dist/PDFApps-macOS.dmg | |
| appimage: | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.10' | |
| - name: Install AppImage build deps | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libfuse2 libegl1 libxkbcommon0 libxcb-cursor0 wget | |
| - name: Build AppImage | |
| run: | | |
| chmod +x appimage/build.sh | |
| ./appimage/build.sh | |
| - name: Upload AppImage | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: PDFApps-AppImage | |
| path: PDFApps-*.AppImage | |
| release: | |
| needs: [build, appimage] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Generate GitHub App token | |
| id: app-token | |
| uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| with: | |
| client-id: ${{ secrets.AP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Generate release notes | |
| id: notes | |
| run: | | |
| TAG="${GITHUB_REF_NAME}" | |
| PREV=$(git tag --sort=-v:refname | grep -E '^v[0-9]' | sed -n '2p') | |
| if [ -z "$PREV" ]; then PREV=$(git rev-list --max-parents=0 HEAD); fi | |
| echo "Generating notes from $PREV to $TAG" | |
| # Collect conventional commit messages, skip chore/ci/deps/merge | |
| FEATURES="" | |
| FIXES="" | |
| PERF="" | |
| OTHER="" | |
| while IFS= read -r line; do | |
| # Skip empty, merge, chore, ci, deps, bump, dependabot | |
| echo "$line" | grep -qiE '^(chore|ci|build|Merge|docs\(deps\))' && continue | |
| echo "$line" | grep -qiE 'dependabot|bump.*from.*to' && continue | |
| echo "$line" | grep -qi '^$' && continue | |
| # Clean up: remove scope prefix for display, capitalize | |
| clean=$(echo "$line" | sed -E 's/^(feat|fix|perf|a11y|docs)(\([^)]*\))?:\s*//') | |
| clean="$(echo "${clean:0:1}" | tr '[:lower:]' '[:upper:]')${clean:1}" | |
| if echo "$line" | grep -qE '^feat'; then | |
| FEATURES="${FEATURES}- ${clean}\n" | |
| elif echo "$line" | grep -qE '^fix'; then | |
| FIXES="${FIXES}- ${clean}\n" | |
| elif echo "$line" | grep -qE '^perf'; then | |
| PERF="${PERF}- ${clean}\n" | |
| elif echo "$line" | grep -qE '^a11y'; then | |
| FIXES="${FIXES}- ${clean}\n" | |
| elif echo "$line" | grep -qE '^docs'; then | |
| : # skip docs-only commits | |
| else | |
| OTHER="${OTHER}- ${clean}\n" | |
| fi | |
| done <<< "$(git log --pretty=format:'%s' "${PREV}..${TAG}" --no-merges)" | |
| BODY="" | |
| if [ -n "$FEATURES" ]; then | |
| BODY="${BODY}## New features\n${FEATURES}\n" | |
| fi | |
| if [ -n "$PERF" ]; then | |
| BODY="${BODY}## Performance\n${PERF}\n" | |
| fi | |
| if [ -n "$FIXES" ]; then | |
| BODY="${BODY}## Fixes & improvements\n${FIXES}\n" | |
| fi | |
| if [ -n "$OTHER" ]; then | |
| BODY="${BODY}## Other\n${OTHER}\n" | |
| fi | |
| if [ -z "$BODY" ]; then | |
| BODY="Bug fixes and improvements." | |
| fi | |
| # Write to file (multiline output) | |
| printf '%b' "$BODY" > release_notes.md | |
| echo "Generated release notes:" | |
| cat release_notes.md | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts | |
| - name: Append SHA256 checksums to release notes | |
| run: | | |
| # The in-app auto-updater parses lines of the form | |
| # "<64-hex> <filename>" from the release body and refuses to | |
| # run an update whose asset hash is not published. Any artifact | |
| # that is uploaded below MUST also appear here. | |
| echo "" >> release_notes.md | |
| echo "## Checksums (SHA256)" >> release_notes.md | |
| for f in artifacts/PDFApps-Windows/PDFAppsSetup.exe \ | |
| artifacts/PDFApps-Windows/PDFApps.exe \ | |
| artifacts/PDFApps-Linux/PDFApps-Linux.tar.gz \ | |
| artifacts/PDFApps-macOS/PDFApps-macOS.dmg \ | |
| artifacts/PDFApps-AppImage/PDFApps-*.AppImage; do | |
| for match in $f; do | |
| if [ -f "$match" ]; then | |
| sha=$(sha256sum "$match" | awk '{print $1}') | |
| name=$(basename "$match") | |
| echo "${sha} ${name}" >> release_notes.md | |
| fi | |
| done | |
| done | |
| echo "Final release notes:" | |
| cat release_notes.md | |
| - name: Create Release | |
| uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v2 | |
| with: | |
| token: ${{ steps.app-token.outputs.token }} | |
| body_path: release_notes.md | |
| files: | | |
| artifacts/PDFApps-Windows/PDFAppsSetup.exe | |
| artifacts/PDFApps-Windows/PDFApps.exe | |
| artifacts/PDFApps-Linux/PDFApps-Linux.tar.gz | |
| artifacts/PDFApps-macOS/PDFApps-macOS.dmg | |
| artifacts/PDFApps-AppImage/PDFApps-*.AppImage |