Skip to content

feat: v2.10 — resolve all 7 Critical findings from 2026-04-18 audit #13

feat: v2.10 — resolve all 7 Critical findings from 2026-04-18 audit

feat: v2.10 — resolve all 7 Critical findings from 2026-04-18 audit #13

Workflow file for this run

# ============================================================
# Bulk PDF Generator — GitHub Actions Release Pipeline
# ============================================================
# Triggered by pushing a version tag (e.g. v2.6).
# Builds Windows .exe and macOS .app in parallel, then creates
# a GitHub Release with both binaries attached.
#
# Usage:
# git tag v2.6
# git push origin --tags
# ============================================================
name: Build & Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
# ── Windows .exe build ──────────────────────────────────────
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for _generate_version.py
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Bake version info
run: python _generate_version.py
- name: Build .exe
run: python -m PyInstaller BulkPDFGenerator.spec --clean --noconfirm
- name: Verify build output and rename
run: |
if (!(Test-Path "dist/Bulk PDF Generator.exe")) {
Write-Error "Build failed — .exe not found"
exit 1
}
# Rename to dotted form so GitHub Release download URLs match
Rename-Item "dist/Bulk PDF Generator.exe" "Bulk.PDF.Generator.exe"
$size = (Get-Item "dist/Bulk.PDF.Generator.exe").Length / 1MB
Write-Host "Built: Bulk.PDF.Generator.exe ($([math]::Round($size, 1)) MB)"
shell: pwsh
- uses: actions/upload-artifact@v4
with:
name: windows-exe
path: dist/Bulk.PDF.Generator.exe
retention-days: 1
# ── macOS .app build ────────────────────────────────────────
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Install create-dmg
run: brew install create-dmg
- name: Bake version info
run: python _generate_version.py
- name: Inject version into mac spec
env:
TAG_NAME: ${{ github.ref_name }}
run: |
TAG="${TAG_NAME#v}" # v2.7.3 → 2.7.3
SHORT="${TAG%.*}" # 2.7.3 → 2.7
# Guard against two-segment tags (v2.8 → TAG=2.8, SHORT=2)
if [[ "$SHORT" == *"."* ]]; then : ; else SHORT="$TAG"; fi
sed -i '' "s/'CFBundleVersion': '[^']*'/'CFBundleVersion': '$TAG'/" BulkPDFGenerator_mac.spec
sed -i '' "s/'CFBundleShortVersionString': '[^']*'/'CFBundleShortVersionString': '$SHORT'/" BulkPDFGenerator_mac.spec
- name: Build .app
run: python -m PyInstaller BulkPDFGenerator_mac.spec --clean --noconfirm
- name: Ad-hoc code sign
run: codesign --force --deep --sign - "dist/Bulk PDF Generator.app"
- name: Create .dmg installer
run: |
if [ ! -d "dist/Bulk PDF Generator.app" ]; then
echo "Build failed — .app not found"
exit 1
fi
set +e
create-dmg \
--volname "Bulk PDF Generator" \
--volicon "icon.icns" \
--background "dmg_background.png" \
--window-pos 200 120 \
--window-size 600 400 \
--icon-size 80 \
--icon "Bulk PDF Generator.app" 150 200 \
--app-drop-link 450 200 \
--no-internet-enable \
"dist/Bulk.PDF.Generator.macOS.dmg" \
"dist/Bulk PDF Generator.app"
EXIT_CODE=$?
set -e
if [ $EXIT_CODE -ne 0 ] && [ $EXIT_CODE -ne 2 ]; then
echo "create-dmg failed with exit code $EXIT_CODE"
exit $EXIT_CODE
fi
SIZE=$(du -h "dist/Bulk.PDF.Generator.macOS.dmg" | cut -f1)
echo "Built: Bulk.PDF.Generator.macOS.dmg ($SIZE)"
- uses: actions/upload-artifact@v4
with:
name: macos-app
path: dist/Bulk.PDF.Generator.macOS.dmg
retention-days: 1
# ── Create GitHub Release ───────────────────────────────────
release:
needs: [build-windows, build-macos]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/download-artifact@v4
with:
name: windows-exe
path: artifacts/
- uses: actions/download-artifact@v4
with:
name: macos-app
path: artifacts/
# ── Build release notes ──────────────────────────────────
# Auto-generated from conventional commits. RELEASE_NOTES.md
# is an optional override for when you want custom wording.
- name: Build release notes
id: notes
run: |
NOTES=""
# Check for manual override in RELEASE_NOTES.md
if [ -f RELEASE_NOTES.md ]; then
NOTES=$(sed '/^<!--/,/-->$/d' RELEASE_NOTES.md | sed '/^$/N;/^\n$/d' | sed -e '/^[[:space:]]*$/d')
fi
# Primary path: auto-generate from conventional commits
if [ -z "$NOTES" ]; then
PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${{ github.ref_name }}$" | head -1)
if [ -n "$PREV_TAG" ]; then
NOTES=$(git log "$PREV_TAG".."${{ github.ref_name }}" --pretty=format:"- %s" \
| grep -v "^- docs:" \
| grep -v "^- chore:" \
| grep -v "^- ci:" \
| grep -v "^- merge:" \
| sed 's/^- feat:/- **New:**/' \
| sed 's/^- fix:/- **Fixed:**/' \
| sed 's/^- build:/- **Build:**/' \
| sed 's/^- perf:/- **Performance:**/' \
| sed 's/^- test:/- **Test:**/' \
| sed 's/^- refactor:/- **Improved:**/')
fi
fi
# Final fallback
if [ -z "$NOTES" ]; then
NOTES="Bug fixes and improvements."
fi
# Set multiline output using heredoc delimiter
EOF_MARKER=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "release_notes<<$EOF_MARKER" >> "$GITHUB_OUTPUT"
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "$EOF_MARKER" >> "$GITHUB_OUTPUT"
# ── Detect pre-release from tag ──────────────────────────
- name: Detect pre-release
id: prerelease
run: |
TAG="${{ github.ref_name }}"
if [[ "$TAG" == *"-beta"* ]] || [[ "$TAG" == *"-rc"* ]] || [[ "$TAG" == *"-alpha"* ]]; then
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
echo "Detected pre-release tag: $TAG"
else
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
fi
# ── Assemble and create release ──────────────────────────
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: false
prerelease: ${{ steps.prerelease.outputs.is_prerelease }}
body: |
## ⬇️ Download
### 🪟 Windows
**[Bulk.PDF.Generator.exe](https://github.com/mrdavearms/bulk-pdf-extractor-and-generator/releases/download/${{ github.ref_name }}/Bulk.PDF.Generator.exe)** — Double-click to run. No installation needed.
> **Windows security prompt:** Windows may show a "Windows protected your PC" screen. This is normal for newly released apps. Click **More info → Run anyway** to proceed.
### 🍎 macOS
**[Bulk.PDF.Generator.macOS.dmg](https://github.com/mrdavearms/bulk-pdf-extractor-and-generator/releases/download/${{ github.ref_name }}/Bulk.PDF.Generator.macOS.dmg)** — Open the disk image and drag the app to Applications.
> **First launch (macOS 14 and earlier):** Right-click the app → Open → click **Open** in the dialog.
> **First launch (macOS 15+):** If blocked, go to **System Settings → Privacy & Security**, scroll down to the "Bulk PDF Generator was blocked" message, and click **Open Anyway**.
---
## What's new in ${{ github.ref_name }}
${{ steps.notes.outputs.release_notes }}
---
<details>
<summary>Technical details</summary>
Built by GitHub Actions from commit `${{ github.sha }}`.
</details>
files: |
artifacts/Bulk.PDF.Generator.exe
artifacts/Bulk.PDF.Generator.macOS.dmg
# ── Clear RELEASE_NOTES.md so it's ready for the next release ──
- name: Clear release notes
run: |
cat > RELEASE_NOTES.md << 'TEMPLATE'
<!-- Release notes for the next tagged release.
The GitHub Actions workflow reads this file and injects it into the
release page. Write in plain English for teachers — no jargon.
If this file is empty (just this comment), release notes are
auto-generated from conventional commit messages. You only need
to write here if you want custom wording for a release.
Format: Markdown bullet list. Bold the headline, em-dash, then explain.
Example:
- **New feature name** — What it does and why teachers care.
- **Fixed: bug description** — What was broken and how it's fixed.
-->
TEMPLATE
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add RELEASE_NOTES.md
git diff --cached --quiet || git commit -m "ci: clear release notes after ${{ github.ref_name }} publish"
git push origin HEAD:main