Skip to content

Run in CI

Run the same container in CI that you test locally. Build the documentation first, mount its static output read-only, and publish the PDF directory as a pipeline artifact.

build the docs → inspect Site Build → export manuals → publish PDFs

The export should run only after the documentation production build succeeds, and mounts the directory that build produced.

Each supported framework has a repository carrying a runnable project and the workflow that exports it on every push — a complete pipeline to copy rather than a fragment:

FrameworkRepository
DocusaurusWorking Docusaurus integration — manuscrio/docusaurus-pdf-export
StarlightWorking Starlight integration — manuscrio/starlight-pdf-export
MkDocs MaterialWorking MkDocs Material integration — manuscrio/mkdocs-pdf-export

All three drive the image with the manuscrio-pdf command rather than docker run, because a GitHub-hosted runner already has Node and the command supplies the mounts and the ownership flags for you. Name your own build directory — the three above produce build, dist and site:

- run: npm ci && npm run build # MkDocs: pip install -r requirements.txt && mkdocs build
- name: Export the docs to PDF
run: npx --yes [email protected] export build --output-dir manuscrio-output
- uses: actions/upload-artifact@v7
with:
name: manual
path: manuscrio-output/*.pdf

Pin the version, as above. One manuscrio version resolves to exactly one image, so re-running an old pipeline renders what it rendered the first time.

There is no Manuscrio plugin or GitHub Action to install, and nothing to add to your documentation site. The rest of this page is the container form: what to use on a runner without Node, or wherever you would rather every path were written out.

For CI systems that store multiline secrets as text, expose the signed licence through MANUSCRIO_PDF_LICENSE:

Terminal window
mkdir -p manuscrio-output
docker run --rm --init --ipc=host \
--user "$(id -u):$(id -g)" \
-e MANUSCRIO_PDF_LICENSE \
-v "$PWD/build:/input:ro" \
-v "$PWD/manuscrio-output:/output" \
ghcr.io/manuscrio/manuscrio-pdf:1.0.0 export /input --output-dir /output

If the CI system writes secrets to files, mount the file the runner wrote and point MANUSCRIO_PDF_LICENSE_FILE at its in-container path. Below, the runner supplies the host path through MANUSCRIO_PDF_LICENSE_HOST_FILE; the first line validates and gives it the shorter local name licence_file. MANUSCRIO_PDF_LICENSE_FILE is the separate, in-container path Manuscrio reads:

Terminal window
licence_file=${MANUSCRIO_PDF_LICENSE_HOST_FILE:?the CI runner must provide the licence file path}
mkdir -p manuscrio-output
docker run --rm --init --ipc=host \
--user "$(id -u):$(id -g)" \
-e MANUSCRIO_PDF_LICENSE_FILE=/run/secrets/manuscrio-license \
-v "$PWD/build:/input:ro" \
-v "$licence_file:/run/secrets/manuscrio-license:ro" \
-v "$PWD/manuscrio-output:/output" \
ghcr.io/manuscrio/manuscrio-pdf:1.0.0 export /input --output-dir /output

A job with no licence still succeeds and produces complete manuals carrying the evaluation watermark. A lapsed subscription does not remove a licence automatically: the existing file is honoured during its 30-day grace period and then fails. Returning to Evaluation Mode requires deliberately removing the licence.

Pass --user "$(id -u):$(id -g)", as above. Without it the PDFs belong to the image’s own user, and a later step that tries to move or archive them may not be able to. The container invocation page covers the other flags.

PDF file names come from manual titles, so they contain spaces and change when an editor retitles a manual. Quote the paths in archive steps, and do not build a downstream contract on the names — see manual IDs and output file names.

Manuscrio renders up to four manuals in parallel by default. Set a lower value when the runner is memory constrained:

Terminal window
mkdir -p manuscrio-output
docker run --rm --init --ipc=host \
--user "$(id -u):$(id -g)" \
-v "$PWD/build:/input:ro" \
-v "$PWD/manuscrio-output:/output" \
ghcr.io/manuscrio/manuscrio-pdf:1.0.0 export /input --concurrency 2 --output-dir /output

Concurrency is the control that matters most, because each parallel render is a browser. Published CPU, memory, and timeout envelopes are not part of the Public Preview yet; size the runner against your own largest manual and keep that measurement with the pipeline.

Do not pass --no-fail-on-clipped in a release pipeline. The default clipping check prevents a successful job from publishing a manual with content silently cut off. When an export fails explains what the failure output means and which failures are contained to a single manual.

Pin the container image to the immutable reference supplied for the release. This keeps local, CI, and rerun behavior aligned.