Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ jobs:
- Python all compiles
- The interview file is minimally correct (python code blocks compile, mako statements compile, we are using known Docassemble keys in the YAML)
- Absolute URLs in `docassemble/*/data/questions` do not return HTTP 404 (excluding `example.com` links)
- PDF templates under `docassemble/*/data/templates` are checked for PDF/UA-1 accessibility using [veraPDF](https://verapdf.org/)

#### Usage

Expand All @@ -308,8 +309,16 @@ jobs:
ignore-urls: |
https://example.com/known-flaky-endpoint
https://another.example.org/blocked-from-ci
# Optional: warning (default), error, or off to skip PDF checking
pdf-validation-mode: "warning"
# Optional: enforce form-field annotation structure rules
pdf-strict: "false"
```

#### PDF Accessibility Checking

PDF templates under `docassemble/*/data/templates` are checked against the PDF/UA-1 accessibility standard. Results are written to the job summary and emitted as annotations. Set `pdf-validation-mode` to `error` to fail on accessibility failures, or `off` to skip the check and veraPDF installation. Set `pdf-strict` to `true` to enforce form-annotation and tab-order rules that are suppressed by default because many tools flatten forms before users see them.

## Development Details

Using [codeql-action](https://github.com/github/codeql-action) as
Expand Down
138 changes: 138 additions & 0 deletions da_build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ inputs:
'error' fails the build when a template has accessibility errors.
'off' skips the check entirely.
default: "warning"
pdf-validation-mode:
description: >-
How to report PDF/UA-1 accessibility failures, found by veraPDF, in PDF
templates under docassemble/*/data/templates.
'warning' annotates the job without failing it (default).
'error' fails the build when a template has accessibility failures.
'off' skips the check and the veraPDF install entirely.
default: "warning"
pdf-strict:
description: >-
Enable strict PDF/UA-1 checking.
When 'false' (default), tab-order and annotation structure rules for form
fields are suppressed because forms are often flattened before users see
them. Set to 'true' to treat those rules as failures.
default: "false"

runs:
using: composite
Expand Down Expand Up @@ -154,3 +169,126 @@ runs:
echo "::warning title=URL checker::$escaped"
fi
shell: bash

- name: Install veraPDF
continue-on-error: true
env:
PDF_VALIDATION_MODE: ${{ inputs.pdf-validation-mode }}
run: |
if [ "$PDF_VALIDATION_MODE" = "off" ]; then
echo "PDF accessibility checking is off; skipping veraPDF installation"
exit 0
fi

# veraPDF 1.28+ is required for compatibility with Java 21 (GitHub Actions default).
VERAPDF_VERSION="1.28.1"
VERAPDF_MINOR="1.28"
INSTALL_DIR="${RUNNER_TEMP}/verapdf"
INSTALLER_SRC="$(mktemp -d "${RUNNER_TEMP}/verapdf-installer-src.XXXXXX")"
VERAPDF_KEY_URL="https://software.verapdf.org/keys/KEY"
VERAPDF_KEY_FINGERPRINT="13DD102B4DD69354D12DE5A83184863278B17FE7"

if command -v verapdf &>/dev/null; then
existing_version="$(
verapdf --version 2>&1 |
grep -Eo '[0-9]+(\.[0-9]+)+' |
head -1 || true
)"
if [ -n "$existing_version" ] && \
[ "$(printf '%s\n' "$VERAPDF_VERSION" "$existing_version" | sort -V | head -1)" = "$VERAPDF_VERSION" ]; then
echo "veraPDF already available: $(verapdf --version 2>&1 | head -1)"
exit 0
fi
echo "Existing veraPDF (${existing_version:-unknown}) is older than required (${VERAPDF_VERSION}); installing ${VERAPDF_VERSION}..."
fi

echo "Downloading veraPDF ${VERAPDF_VERSION}..."
INSTALLER_ZIP="${RUNNER_TEMP}/verapdf-installer.zip"
INSTALLER_SIGNATURE="${INSTALLER_ZIP}.asc"
VERAPDF_KEY="${RUNNER_TEMP}/verapdf-key.asc"
wget -q --https-only \
"https://software.verapdf.org/releases/${VERAPDF_MINOR}/verapdf-greenfield-${VERAPDF_VERSION}-installer.zip" \
-O "${INSTALLER_ZIP}"
wget -q --https-only \
"https://software.verapdf.org/releases/${VERAPDF_MINOR}/verapdf-greenfield-${VERAPDF_VERSION}-installer.zip.asc" \
-O "${INSTALLER_SIGNATURE}"
wget -q --https-only "${VERAPDF_KEY_URL}" -O "${VERAPDF_KEY}"

# Verify the publisher key before using it to verify the installer.
export GNUPGHOME="$(mktemp -d "${RUNNER_TEMP}/verapdf-gnupg.XXXXXX")"
if ! gpg --batch --quiet --with-colons --show-keys "${VERAPDF_KEY}" |
awk -F: -v expected="${VERAPDF_KEY_FINGERPRINT}" \
'$1 == "fpr" && toupper($10) == expected { found = 1 } END { exit found ? 0 : 1 }'; then
echo "Downloaded veraPDF signing key has an unexpected fingerprint" >&2
exit 1
fi
gpg --batch --quiet --import "${VERAPDF_KEY}"

GPG_STATUS="${RUNNER_TEMP}/verapdf-gpg-status.txt"
GPG_ERROR="${RUNNER_TEMP}/verapdf-gpg-error.txt"
if ! gpg --batch --status-fd=1 --verify "${INSTALLER_SIGNATURE}" "${INSTALLER_ZIP}" \
>"${GPG_STATUS}" 2>"${GPG_ERROR}"; then
cat "${GPG_ERROR}" >&2
echo "veraPDF installer signature verification failed" >&2
exit 1
fi
if ! awk -v expected="${VERAPDF_KEY_FINGERPRINT}" \
'$1 == "[GNUPG:]" && $2 == "VALIDSIG" && (toupper($3) == expected || toupper($NF) == expected) { found = 1 } END { exit found ? 0 : 1 }' \
"${GPG_STATUS}"; then
echo "veraPDF installer was not signed by the pinned publisher key" >&2
exit 1
fi

unzip -q "${INSTALLER_ZIP}" -d "${INSTALLER_SRC}"

cat > "${RUNNER_TEMP}/verapdf-autoinstall.xml" << EOF
<AutomatedInstallation langpack="eng">
<com.izforge.izpack.panels.htmlhello.HTMLHelloPanel id="welcome"/>
<com.izforge.izpack.panels.target.TargetPanel id="install_dir">
<installpath>${INSTALL_DIR}</installpath>
</com.izforge.izpack.panels.target.TargetPanel>
<com.izforge.izpack.panels.packs.PacksPanel id="sdk_pack_select">
<pack name="veraPDF GUI" selected="true"/>
<pack name="veraPDF Mac and *nix Scripts" selected="true"/>
<pack name="veraPDF Batch files" selected="false"/>
<pack name="veraPDF Validation model" selected="true"/>
<pack name="veraPDF Documentation" selected="false"/>
<pack name="veraPDF Sample Plugins" selected="false"/>
</com.izforge.izpack.panels.packs.PacksPanel>
<com.izforge.izpack.panels.install.InstallPanel id="install"/>
<com.izforge.izpack.panels.finish.FinishPanel id="finish"/>
</AutomatedInstallation>
EOF

INSTALLER_JAR=$(find "${INSTALLER_SRC}" -name "verapdf-izpack-installer-${VERAPDF_VERSION}.jar" -print -quit)
if [ -z "${INSTALLER_JAR}" ]; then
echo "Could not find the expected veraPDF ${VERAPDF_VERSION} installer JAR" >&2
exit 1
fi
java -jar "${INSTALLER_JAR}" "${RUNNER_TEMP}/verapdf-autoinstall.xml"
installed_version="$(
"${INSTALL_DIR}/verapdf" --version 2>&1 |
grep -Eo '[0-9]+(\.[0-9]+)+' |
head -1 || true
)"
if [ -z "${installed_version}" ] || \
[ "$(printf '%s\n' "$VERAPDF_VERSION" "$installed_version" | sort -V | head -1)" != "$VERAPDF_VERSION" ]; then
echo "Installed veraPDF version (${installed_version:-unknown}) is older than required (${VERAPDF_VERSION})" >&2
exit 1
fi
echo "${INSTALL_DIR}" >> "${GITHUB_PATH}"
echo "Installed veraPDF: $("${INSTALL_DIR}/verapdf" --version 2>&1 | head -1)"
shell: bash

- name: Check PDF accessibility with veraPDF
env:
PDF_VALIDATION_MODE: ${{ inputs.pdf-validation-mode }}
PDF_ACCESSIBILITY_MODE: ${{ inputs.pdf-validation-mode }}
PDF_ACCESSIBILITY_STRICT: ${{ inputs.pdf-strict }}
run: |
if [ "$PDF_VALIDATION_MODE" = "off" ]; then
echo "PDF accessibility checking is off"
exit 0
fi
python "${{ github.action_path }}/check_pdf_accessibility.py"
shell: bash
Loading