feat: v2.10 — resolve all 7 Critical findings from 2026-04-18 audit #13
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
| # ============================================================ | |
| # 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 |