1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
|
name: build
on: # cf. https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662
push:
branches:
- master
pull_request:
branches:
- master
jobs:
check-reference-pdf-files:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Checkout ποΈ
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python 3.14 π§
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Install system dependencies βοΈ
run: sudo apt-get update --allow-releaseinfo-change && sudo apt-get install qpdf
- name: Check all PDF reference files used in tests β
run: |
# Using qpdf
find . -name '*.pdf' | xargs -n 1 sh -c 'qpdf --check --password=fpdf2 $0 || exit 255'
export PYTHONPATH=$PWD
# Using Datalogics PDF Checker:
scripts/install-pdfchecker.sh
time scripts/pdfchecker.py --process-all-test-pdf-files
scripts/pdfchecker.py --print-aggregated-report
# Using VeraPDF:
scripts/install-verapdf.sh
time scripts/verapdf.py --process-all-test-pdf-files
scripts/verapdf.py --print-aggregated-report
scripts/check-PDF-A-with-verapdf.sh
lint:
runs-on: ubuntu-24.04
permissions:
security-events: write # for zizmor
steps:
- name: Checkout ποΈ
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python 3.13 π§
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.13 # guarddog / pygit2 still have issues with 3.14
- name: Install Python dependencies βοΈ
run: |
python -m pip install --upgrade pip setuptools wheel
pip install --upgrade .[dev,test]
# Running zizmor 1st, because it is blocking PR merge
- name: Run zizmor π
run: zizmor --format=sarif . > results.sarif
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
sarif_file: results.sarif
category: zizmor
- name: Run linters π
run: |
black --check .
pylint fpdf test tutorial/tuto*.py
bandit -c .banditrc.yml -r contributors/ fpdf/ tutorial/
semgrep scan --config auto --error --strict --exclude-rule=python.lang.security.insecure-hash-function.insecure-hash-function fpdf
- name: Run type checkers π
run: |
mypy
pyright
- name: Scan project with grype π
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
with:
path: "."
fail-build: true
- name: Scan project dependencies with guarddog πΆ
# GuardDog is very slow, we only execute it on the master branch:
if: github.ref == 'refs/heads/master'
run: |
pip install guarddog
python scripts/generate-dependencies-list.py pyproject.toml fpdf2-full-requirements.txt
time guarddog pypi verify fpdf2-full-requirements.txt
time guarddog pypi verify contributors/requirements.txt
- name: Spell Check Repo βοΈ
uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1.44.0
test:
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
permissions: {}
steps:
- name: Checkout ποΈ
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }} π§
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Install system dependencies βοΈ
if: ${{ startsWith(matrix.platform, 'ubuntu') }}
# Ghostscript is needed for test/table/test_table_extraction.py
run: sudo apt-get update --allow-releaseinfo-change && sudo apt-get install ghostscript
- name: Install qpdf βοΈ
# We run the unit tests WITHOUT qpdf for a single parallel execution / Python version:
if: ${{ startsWith(matrix.platform, 'ubuntu') && matrix.python-version != '3.13' }}
run: sudo apt-get update --allow-releaseinfo-change && sudo apt-get install qpdf
- name: Install qpdf on Windows
if: startsWith(matrix.platform, 'windows')
shell: pwsh
run: |
choco install qpdf -y
qpdf --version
- name: Install Python dependencies βοΈ
# Temporarily writing a constraints file to make sure numpy 2.3.3 is used
# for python 3.14
shell: bash
run: |
python -m pip install --upgrade pip setuptools wheel
# --- Create constraints file ---
# For Python 3.14 only - temporary solution while camelot-py
# update its dependencies
CONSTRAINTS_FILE="constraints.txt"
if [[ "${{ matrix.python-version }}" == "3.14" ]]; then
echo "numpy>=2.3.3" > "$CONSTRAINTS_FILE"
echo "Added numpy>=2.3.3 to $CONSTRAINTS_FILE"
else
: > "$CONSTRAINTS_FILE" # create empty file
echo "Created empty $CONSTRAINTS_FILE"
fi
# --- Install dependencies respecting constraints ---
pip install --upgrade .[dev,test] -c "$CONSTRAINTS_FILE"
- name: Run tests β
shell: bash
env:
CHECK_EXEC_TIME: ${{ matrix.python-version == '3.10' && 'test-enabled' || '' }}
CHECK_RSS_MEMORY: ${{ matrix.python-version == '3.14' && 'test-enabled' || '' }}
run: |
# Ensuring there is no `generate=True` left remaining in calls to assert_pdf_equal:
grep -IRF generate=True test/ && exit 1
# Executing all tests:
if [[ "${{ matrix.platform }}" == "ubuntu-latest" ]]; then
echo "Running with --trace-memory-usage (Ubuntu)"
pytest -vv --trace-memory-usage
else
echo "Running without trace-memory-usage (${{ matrix.platform }})"
pytest -vv
fi
- name: Upload coverage report to codecov.io β
# We only upload coverage ONCE, for a single parallel execution / Python version:
if: ${{ startsWith(matrix.platform, 'ubuntu') && matrix.python-version == '3.13' }}
run: bash <(curl -s https://codecov.io/bash)
- name: Run tests with the minimal versions of fpdf2 direct dependencies β
if: ${{ startsWith(matrix.platform, 'ubuntu') && matrix.python-version == '3.10' }}
run: |
# Ensuring that those minimal versions remain compatible:
sed -i '/requires-python/s/>=/~=/' pyproject.toml
sed -i 's/>=/==/' pyproject.toml
# Removing field not supported in pyproject.toml at the time (prevent this: ValueError - invalid pyproject.toml config):
sed -i '/license.*=/d' pyproject.toml
pip install .
# Targeting only a subset of tests because: A) it's faster and B) some tests are dependant on a specific version of fonttools or Pillow
# Deselect test involving TTF fonts due to differences with old fonttools version.
pytest -vv test/barcodes test/drawing test/errors test/image/test_load_image.py test/metadata test/shapes test/signing test/text_region test/utils --deselect test/drawing/test_drawing_text.py::test_text_ttf_transforms
doc:
runs-on: ubuntu-latest
permissions:
# required to deploy doc, by pushing commits to branch gh-pages:
contents: write
pages: write
steps:
- name: Harden Runner
# Security hardening because this is a sensitive job,
# where extra care should be taken NOT to leak any secret
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: block
allowed-endpoints:
github.com:443
api.github.com:443
*.githubusercontent.com:443
pypi.org:443
files.pythonhosted.org:443
registry.npmjs.org:443
api.star-history.com:443
cdnjs.cloudflare.com:443
colab.research.google.com:443
fonts.googleapis.com:443
fonts.gstatic.com:443
img.shields.io:443
unpkg.com:443
borbpdf.com:443
# Starting from api.star-history.com, the endpoints whitelist reason is the mkdocs privacy plugin
- name: Checkout ποΈ
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# Required for mkdocs git-revision-date-localized plugin:
fetch-depth: 0
- name: Set up Python 3.14 π§
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: 3.14
- name: Install Python dependencies βοΈ
run: |
python -m pip install --upgrade pip setuptools wheel
pip install --upgrade .[docs] -r contributors/requirements.txt
- name: Generate HTML documentation ποΈ
run: |
mkdir -p public/
# Setting PDF manual version:
VERSION=$(python -c "import importlib.metadata; print(importlib.metadata.version('fpdf2'))")
sed -i "s/author:.*/author: v$VERSION/" mkdocs.yml
cp tutorial/notebook.ipynb docs/
python -m mkdocs build
pdoc --html -o public/ fpdf --template-dir docs/pdoc
scripts/add_pdoc_to_search_index.py
- name: Build contributors map πΊοΈ
# As build_contributors_html_page.py can hang due to GitHub rate-limiting, we only execute this on master for now
if: github.ref == 'refs/heads/master'
env:
# Needed by contributors/build_contributors_html_page.py:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PYTHONUNBUFFERED: 1
run: |
cd contributors/
npm ci
./build_contributors_html_page.py py-pdf/fpdf2
cp -t ../public/ contributors.html contributors-map-small.png
- name: Deploy documentation π
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: public/
publish:
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/fpdf2
permissions:
# Trusted publishing configured there: https://pypi.org/manage/project/fpdf2/settings/publishing/
id-token: write
steps:
- name: Harden Runner
# Security hardening because this is a sensitive job,
# where extra care should be taken NOT to leak any secret
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: block
allowed-endpoints:
ghcr.io:443
github.com:443
*.githubusercontent.com:443
pypi.org:443
files.pythonhosted.org:443
*.sigstore.dev:443
upload.pypi.org:443
- name: Checkout ποΈ
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Set up Python 3.14 π§
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: Build distributions for Pypi ποΈ
id: build
run: |
echo Versions already released on Pypi: $(curl -Ls 'https://pypi.org/pypi/fpdf2/json' | jq -r '.releases|keys[]')
pip install --upgrade build twine .
VERSION=$(python -c "import importlib.metadata; print(importlib.metadata.version('fpdf2'))")
echo Current code version: $VERSION
# Checking if current code version has already been released:
if ! curl -Ls 'https://pypi.org/pypi/fpdf2/json' | jq -r '.releases|keys[]' | grep "^$VERSION\$"; then echo publish=yes >> "$GITHUB_OUTPUT"; python -m build; twine check dist/*; fi
- name: Publish package distributions to PyPI π
if: steps.build.outputs.publish == 'yes'
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
# Doc: https://github.com/marketplace/actions/pypi-publish
with:
print-hash: true
|