Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b7166ea
Add @neo4j-antora/pdf-generator and hook up PDF generation CI
recrwplay Sep 23, 2026
3bc0cb7
Drop the dedicated pdf.yml - reuse an existing playbook via --extension
recrwplay Sep 23, 2026
091ae0d
Use publish.yml consistently for PDF generation, not preview.yml
recrwplay Sep 23, 2026
fdfbd59
Mirror reusable-docs-build.yml's package-script convention
recrwplay Sep 24, 2026
091e0be
Pass the real default Antora extensions to the PDF build, not just pd…
recrwplay Sep 24, 2026
6b2b4cf
Temporarily trigger on push for CI verification (revert after)
recrwplay Sep 24, 2026
e49c801
Temporarily point at published RC for CI verification (revert after)
recrwplay Sep 24, 2026
10da5c1
Revert temporary CI-verification changes (push trigger + RC pin), ver…
recrwplay Sep 24, 2026
2f672dd
Decouple pdf-generator install from docs/package.json dependencies
recrwplay Sep 24, 2026
9709478
Temporarily pin pdf-generator to published RC for CI verification (re…
recrwplay Sep 24, 2026
412befb
Revert "Temporarily pin pdf-generator to published RC for CI verifica…
recrwplay Sep 24, 2026
abf895a
Fix footnotes rendering inline instead of as numbered page notes
recrwplay Sep 24, 2026
ca39b12
Remove table-footnotes handling, made obsolete by the footnote fix
recrwplay Sep 24, 2026
0acc759
Pin antora to 3.2.0 in docs/package.json
recrwplay Oct 1, 2026
f57e76f
Merge branch 'dev' into pdf-generator-package-clean
recrwplay Oct 1, 2026
d7b50e9
Trigger a PDF build alongside the HTML build, and trim the PDF extens…
recrwplay Oct 1, 2026
da31cf0
Remove aliases-redirects from the default extensions of the PDF build
recrwplay Oct 1, 2026
7508f00
Remove selector-labels from the default extensions of the PDF build
recrwplay Oct 1, 2026
938a110
pdf-generator: publish only the intended files, and bump to 0.1.1
recrwplay Oct 1, 2026
21b79c6
PDF build: print and upload the Antora log, as the HTML build does
recrwplay Oct 1, 2026
895186c
pdf-generator: stop footnotes in nested tables from hanging the PDF r…
recrwplay Oct 1, 2026
035b5cc
pdf-generator: keep table captions above the header row inside admoni…
recrwplay Oct 1, 2026
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
30 changes: 30 additions & 0 deletions .github/workflows/docs-generate-pdf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: "Generate PDF"

permissions:
contents: read

# Mirrors docs-generate-html.yml's trigger shape (workflow_dispatch with a
# build-ref input). Deliberately simpler for now: no dev-vs-prod split yet,
# and there's no publish hand-off step - it just uploads the PDF as a build
# artifact until a real publish target exists. Uses a relative path (this
# workflow lives in the same repo as reusable-docs-pdf-build.yml, testing it
# against docs-tools' own docs/ as the fixture) rather than a versioned
# `neo4j/docs-tools/...@v2`-style reference, the same way docs-generate-
# html.yml does for reusable-docs-build.yml.
on:
workflow_dispatch:
inputs:
build-ref:
description: 'The git ref to build from'
type: string
required: true

jobs:

docs-build-pdf:
name: Generate PDF
uses: ./.github/workflows/reusable-docs-pdf-build.yml
with:
docs-dir: 'docs'
build-ref: ${{ inputs.build-ref || github.ref_name }}
fetch-depth: 0
36 changes: 36 additions & 0 deletions .github/workflows/docs-trigger-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ on:
push:
branches:
- 'dev'
# TEMPORARY, while docs-generate-pdf.yml is being tested on its PR (#136): remove this
# branch before merging. docs-tools itself is not published from, so there is no real
# branch list to keep in step with the playbooks here.
- 'pdf-generator-package-clean'
workflow_dispatch:

# Set DEV_BRANCH to your staging branch.
Expand Down Expand Up @@ -139,3 +143,35 @@ jobs:
}

await core.summary.write()

# Trigger docs-generate-pdf.yml as its own separate run too, for the same reasons as
# above: not a workflow_call, so it is fully independent of the HTML builds. If the PDF
# build fails, the HTML builds are unaffected (nothing here `needs` this job, and it
# `needs` nothing), and its artifact can't collide with the HTML "docs" artifact.
# It builds the branch that triggered this run: a PDF is not environment-specific, so
# there is no dev/prod pairing to work out.
trigger-generate-pdf:
name: Trigger PDF build
runs-on: ubuntu-latest
steps:
- name: Dispatch docs-generate-pdf.yml
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
BUILD_REF: ${{ github.ref_name }}
with:
script: |
const buildRef = process.env.BUILD_REF
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'docs-generate-pdf.yml',
// Dispatch from the same branch that is being built, so the workflow file
// that runs is the one on that branch (docs-generate-pdf.yml only exists on
// this branch until its PR is merged).
ref: buildRef,
inputs: {
'build-ref': buildRef,
},
})
core.summary.addRaw(`Triggered \`docs-generate-pdf.yml\` on branch \`${buildRef}\``, true)
await core.summary.write()
203 changes: 203 additions & 0 deletions .github/workflows/reusable-docs-pdf-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
name: Generate PDF

permissions:
contents: read
pull-requests: none

on:
workflow_call:
inputs:
build-ref:
description: 'The git ref (branch or tag) to checkout'
required: false
type: string
default: ''
fetch-depth:
description: 'The git fetch depth: 1 to fetch just the ref (branch or tag), 0 for full history'
required: false
type: number
default: 1
docs-dir:
description: 'The working directory to run jobs in'
required: false
type: string
default: '.'
node-version:
description: 'The Node.js version to use'
required: false
type: string
default: '24'
antora-version:
description: 'The Antora version to install (must be >=3.2.0 - @antora/pdf-extension needs the assembler it bundles from that version on)'
required: false
type: string
default: '3.2.0'
pdf-generator-version:
description: 'The @neo4j-antora/pdf-generator version to install. Not a dependency of the docset''s own package.json - it is only ever needed for this PDF-specific build, so declaring it there would make *every* install in the repo (including totally unrelated jobs, e.g. a plain HTML PR check) fail whenever this version is not yet published, the same way reusable-docs-build.yml installs a specific Antora version itself rather than trusting whatever a docset''s own package.json happens to pin.'
required: false
type: string
default: '0.1.3'
package-script:
description: 'The name of the script to run in package.json, same convention as reusable-docs-build.yml''s own package-script input - the docset is trusted to already have a valid script here (defaults to verify:publish, matching what a real PDF export should reflect). @neo4j-antora/pdf-generator is injected as a one-off --extension flag onto whatever that script already does, so it needs no dedicated PDF playbook or script of its own.'
required: false
type: string
default: 'verify:publish'
antora-extensions:
description: 'Antora extensions to pass to the build script. Defaults to the extensions that affect the content of a PDF, plus @neo4j-antora/pdf-generator itself. It deliberately leaves out the HTML-site-only extensions that reusable-docs-build.yml has (the redirects, version selector labels, sitemaps, page list and unlisted pages ones), and @neo4j-antora/table-footnotes - that one hooks Antora''s own pagesComposed event, which this pipeline''s separate, isolated Asciidoctor conversion never triggers, so it would always be a silent no-op here (see pdf-generator/README.adoc). package-script''s own command has none of these baked in itself (reusable-docs-build.yml supplies them as CLI flags, the same way this workflow does), so without this a PDF build would silently run without them.'
required: false
type: string
default: '
@neo4j-antora/roles-labels
@neo4j-antora/xref-hash-validator
@neo4j-antora/pdf-generator
'
antora-extensions-exclude:
description: 'Antora extensions to NOT pass to the build script, same convention as reusable-docs-build.yml''s own input of the same name - not every docset installs every extension in the antora-extensions default list.'
required: false
type: string
default: 'na'
retain-artifacts:
description: 'The number of days to retain artifacts'
type: number
default: 28

jobs:

build-pdf:
runs-on: ubuntu-latest

env:
NODE_VERSION: ${{ inputs.node-version }}
ANTORA_VERSION: ${{ inputs.antora-version }}
BUILD_REF: ${{ inputs.build-ref || '' }}
FETCH_DEPTH: ${{ inputs.fetch-depth }}
DOCS_DIR: ${{ inputs.docs-dir }}
PDF_GENERATOR_VERSION: ${{ inputs.pdf-generator-version }}
PACKAGE_SCRIPT: ${{ inputs.package-script }}
ANTORA_EXTENSIONS: ${{ inputs.antora-extensions }}
ANTORA_EXTENSIONS_EXCLUDE: ${{ inputs.antora-extensions-exclude }}

steps:

- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ env.BUILD_REF }}
fetch-depth: ${{ env.FETCH_DEPTH }}

- name: Use Node.js version ${{ env.NODE_VERSION }}
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install docset dependencies
working-directory: ${{ env.DOCS_DIR }}
run: |
npm install --omit=dev
# Ensure a correct version of Antora, the same way reusable-docs-build.yml
# does for the HTML build - @antora/pdf-extension needs the assembler
# Antora 3.2.0+ bundles, regardless of what a docset's own package.json
# currently pins.
npm uninstall @antora/cli @antora/site-generator-default || true
npm install "antora@${ANTORA_VERSION}"
# @neo4j-antora/pdf-generator is installed here, on its own, rather than
# being declared in the docset's own package.json - it's only ever
# needed for this PDF-specific build, and a docset's package.json is
# shared by every job that runs npm install against it (e.g. the plain
# HTML PR-check build), so declaring it there would make all of those
# unrelated jobs fail whenever this version isn't published yet. It
# brings in @antora/pdf-extension, the PDF theme/renderer/
# postprocessors, and its own isolated Asciidoctor.js install (via its
# own postinstall) all by itself.
npm install "@neo4j-antora/pdf-generator@${PDF_GENERATOR_VERSION}"

# Mirrors reusable-docs-build.yml's own "Remove excluded extensions" +
# "Create list of extensions for the Antora CLI" steps - not every
# docset installs every extension in the antora-extensions default list,
# so antora-extensions-exclude lets a caller drop the ones it doesn't
# have before turning the rest into --extension X --extension Y ... flags.
- name: Remove excluded extensions
run: |
for extension in $ANTORA_EXTENSIONS; do
if [[ $ANTORA_EXTENSIONS_EXCLUDE =~ "$extension" ]]; then
continue
fi
filtered_extensions=" ${extension} ${filtered_extensions}"
done
echo "ANTORA_FILTERED_EXTENSIONS=${filtered_extensions}" >> $GITHUB_ENV

- name: Create list of extensions for the Antora CLI
run: |
for extension in $ANTORA_FILTERED_EXTENSIONS; do
antora_cli_extensions="--extension ${extension} ${antora_cli_extensions}"
done
echo "ANTORA_CLI_EXTENSIONS=${antora_cli_extensions}" >> $GITHUB_ENV

# Reuses whichever script the docset already runs for a normal build
# (default verify:publish) rather than needing its own dedicated PDF
# script or playbook - the extension list above (which includes
# @neo4j-antora/pdf-generator itself) is appended as trailing CLI flags,
# the same way reusable-docs-build.yml appends its own extensions onto
# $PACKAGE_SCRIPT via `npm run ... --`, rather than the docset's own
# script needing to know about any of them.
- name: Run PDF build
id: run-pdf-build
working-directory: ${{ env.DOCS_DIR }}
continue-on-error: true
env:
# Same fix as reusable-docs-build.yml's "Run package script" step:
# antora requires local-path extensions (e.g. a sibling extensions/
# dir) directly by their real filesystem path, bypassing node_modules
# resolution entirely. NODE_PATH gives Node an extra place to look,
# so those extensions can still find hoisted deps (e.g. vinyl) that
# are only installed under docs-dir's node_modules.
NODE_PATH: ${{ github.workspace }}/${{ env.DOCS_DIR }}/node_modules
run: npm run "$PACKAGE_SCRIPT" -- $ANTORA_CLI_EXTENSIONS

- name: Get the dir that contains the PDF
if: steps.run-pdf-build.outcome == 'success'
working-directory: ${{ env.DOCS_DIR }}
id: get-pdf-path
run: |
# Excludes build/assembler/ - the assembler's own intermediate working
# copy, not the real deliverable - since output.dir (where the real
# one lands) varies by playbook (e.g. build/site vs build/docs).
pdf_path=$(find build -path '*/assembler/*' -prune -o -iname '*.pdf' -print | head -1)
if [ -z "$pdf_path" ]; then
echo "::error::No PDF found under build/ - check the Antora log above"
exit 1
fi
echo "pdf-path=$pdf_path" >> "$GITHUB_OUTPUT"

# Same as reusable-docs-build.yml's "Print Antora log", but also when the build step
# itself reported success yet no PDF was found: the PDF renderer can fail ("Unable
# to generate the PDF") without making the Antora run fail, so that case only shows
# up as the PDF lookup above failing.
- name: Print Antora log
if: always() && (steps.run-pdf-build.outcome == 'failure' || steps.get-pdf-path.outcome == 'failure')
working-directory: ${{ env.DOCS_DIR }}
run: |
if [ -f build/log/log.json ]; then
cat build/log/log.json
else
echo "No log file found at build/log/log.json"
fi

- name: Upload PDF artifact
if: steps.run-pdf-build.outcome == 'success'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: pdf
path: ${{ env.DOCS_DIR }}/${{ steps.get-pdf-path.outputs.pdf-path }}
retention-days: ${{ inputs.retain-artifacts }}

- name: Upload Log artifact
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: antora-log
path: ${{ env.DOCS_DIR }}/build/log
retention-days: ${{ inputs.retain-artifacts }}

- name: Fail job if PDF build failed
if: always() && steps.run-pdf-build.outcome == 'failure'
run: exit 1
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ package-lock.json
node_modules/
.env

# pdf-generator/scripts/setup-vendor.js writes this at install time (see its
# own comment) - node_modules/ and package-lock.json above already cover the
# rest of what that install produces
pdf-generator/vendor/package.json

# Claude — ignore everything except the shared slash commands
.claude/*
!.claude/commands/
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@neo4j-antora/xref-hash-validator": "^0.1.4",
"@neo4j-documentation/macros": "^1.0.4",
"@neo4j-documentation/remote-include": "^1.0.0",
"antora": "3.1.14",
"antora": "3.2.0",
"node-html-parser": "9.0.0"
},
"devDependencies": {
Expand Down
Loading
Loading