diff --git a/.github/workflows/github-ci.yaml b/.github/workflows/github-ci.yaml index dff405fb..d29f90c0 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 @@ -90,3 +95,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 diff --git a/pdfly/check_sign.py b/pdfly/check_sign.py index a4dd76a7..451495d4 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 c7519b52..08849028 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 ] diff --git a/tests/test_booklet.py b/tests/test_booklet.py index 21da9fe1..454b955a 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()]