From 0054734ab9703de8d1fbd506fe093c95b7e59287 Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Sun, 25 Oct 2026 18:42:21 +0100 Subject: [PATCH 1/3] DEV: Add ci-success step in github-ci workflow --- .github/workflows/github-ci.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/github-ci.yaml b/.github/workflows/github-ci.yaml index dff405f..ecf7ca9 100644 --- a/.github/workflows/github-ci.yaml +++ b/.github/workflows/github-ci.yaml @@ -90,3 +90,15 @@ jobs: - name: Test running installed package working-directory: /tmp run: python -c "import pdfly;print(pdfly.__version__)" + + # Single stable check to mark as "required" in the branch protection rules, + # so the matrix can change without touching the repository settings. + ci-success: + name: CI success + if: always() + needs: [tests, codestyle, package] + runs-on: ubuntu-latest + steps: + - name: Fail if any required job did not succeed + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') + run: exit 1 From 5f94591983f8ffe2fbbb6e3d69af699f645defa2 Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Sun, 25 Oct 2026 18:56:30 +0100 Subject: [PATCH 2/3] BUG: Fix import error from oscrypto and pin endesive<3 oscrypto 1.3.0 (latest on PyPI) fails to parse OpenSSL versions with a two-digit patch number (e.g. 3.0.13), raising LibraryNotFoundError when endesive.pdf is imported. Install the fixed upstream commit in CI and import endesive.pdf lazily so only check-sign is affected for users. endesive 3.x changed pdf.verify() to return Result objects, which breaks check-sign. Pin endesive<3 until we migrate. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/github-ci.yaml | 5 +++++ pdfly/check_sign.py | 5 ++++- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-ci.yaml b/.github/workflows/github-ci.yaml index ecf7ca9..d29f90c 100644 --- a/.github/workflows/github-ci.yaml +++ b/.github/workflows/github-ci.yaml @@ -40,6 +40,11 @@ jobs: # We ensure that those minimal versions remain compatible: sed -i '/dependencies = \[/,/\]/s/>=/==/' pyproject.toml pip install . + - name: Install oscrypto with OpenSSL >= 3.0.10 support + # oscrypto 1.3.0 (latest on PyPI) can't parse two-digit OpenSSL patch + # versions: https://github.com/wbond/oscrypto/issues/75 + # The fixed commit still reports 1.3.0, hence --force-reinstall. + run: pip install --force-reinstall --no-deps "oscrypto @ git+https://github.com/wbond/oscrypto.git@1547f535001ba568b239b8797465536759c742a3" - name: Run tests run: pytest -vv diff --git a/pdfly/check_sign.py b/pdfly/check_sign.py index a4dd76a..451495d 100644 --- a/pdfly/check_sign.py +++ b/pdfly/check_sign.py @@ -13,10 +13,13 @@ from pathlib import Path import typer -from endesive import pdf def main(filename: Path, pem: Path, verbose: bool | None) -> None: + # Imported lazily: endesive.pdf pulls in oscrypto, which fails to import + # on some OpenSSL versions. That should only break this command. + from endesive import pdf + x509_certificates = [pem.read_bytes()] results = pdf.verify(filename.read_bytes(), x509_certificates) diff --git a/pyproject.toml b/pyproject.toml index c7519b5..0884902 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "fpdf2>=2.8.1", "asn1crypto", "cryptography", - "endesive", + "endesive<3", # 3.x changed pdf.verify() to return Result objects "requests>=2.32.5", # required by endesive.signer ] From 4e411a664fe9b1d6f87e2f3e02687537d03d784b Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Sun, 25 Oct 2026 19:06:12 +0100 Subject: [PATCH 3/3] TST: Make booklet order test independent of pypdf text layout Since pypdf 6.16.2, extract_text() puts the two halves of a booklet page on separate lines instead of joining them with a space. The test is about page order, so compare the words per output page instead of the exact text. Co-Authored-By: Claude Opus 5.5 --- tests/test_booklet.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_booklet.py b/tests/test_booklet.py index 21da9fe..454b955 100644 --- a/tests/test_booklet.py +++ b/tests/test_booklet.py @@ -106,7 +106,8 @@ def test_booklet_order( ) captured = capsys.readouterr() assert exit_code == 0, captured.err - assert captured.out == expected + assert captured.out.split() == expected.split() + assert page_words(f"output{page_count}.pdf") == page_words_of(expected) exit_code = run_cli( [ @@ -130,4 +131,18 @@ def test_booklet_order( ) captured = capsys.readouterr() assert exit_code == 0, captured.err - assert captured.out == expected_bc + assert captured.out.split() == expected_bc.split() + assert page_words(f"outputbc{page_count}.pdf") == page_words_of( + expected_bc + ) + + +def page_words(filename: str) -> list[list[str]]: + # Compare words rather than raw text: whether pypdf joins the two halves + # of a booklet page with a space or a newline depends on its version. + return [page.extract_text().split() for page in PdfReader(filename).pages] + + +def page_words_of(expected: str) -> list[list[str]]: + """Parse an expectation with one line per output page.""" + return [line.split() for line in expected.splitlines()]