Skip to content

Build & Release

Build & Release #42

Workflow file for this run

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